Deployment
Docker Deployment
Deploy Glueful with Docker
Deploy your Glueful application using Docker containers for consistent, reproducible deployments.
Quick Start
Dockerfile
Dockerfile
:
FROM php:8.2-fpm
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd \
&& pecl install redis \
&& docker-php-ext-enable redis
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www
# Copy application
COPY . /var/www
# Install dependencies
RUN composer install --no-dev --optimize-autoloader --classmap-authoritative
# Set permissions
RUN chown -R www-data:www-data /var/www
EXPOSE 9000
CMD ["php-fpm"]
PostgreSQL users: add the PostgreSQL extension
# If you use PostgreSQL
RUN apt-get update && apt-get install -y libpq-dev \
&& docker-php-ext-install pdo_pgsql \
&& rm -rf /var/lib/apt/lists/*
Docker Compose
docker-compose.yml
:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: glueful-app
restart: unless-stopped
working_dir: /var/www
volumes:
- ./:/var/www
- ./storage:/var/www/storage
networks:
- glueful
nginx:
image: nginx:alpine
container_name: glueful-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./docker/nginx:/etc/nginx/conf.d
networks:
- glueful
db:
image: mysql:8.0
container_name: glueful-db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
volumes:
- dbdata:/var/lib/mysql
networks:
- glueful
redis:
image: redis:alpine
container_name: glueful-redis
restart: unless-stopped
networks:
- glueful
worker:
build:
context: .
dockerfile: Dockerfile
container_name: glueful-worker
restart: unless-stopped
command: php glueful queue:work
volumes:
- ./:/var/www
networks:
- glueful
depends_on:
- app
- redis
networks:
glueful:
driver: bridge
volumes:
dbdata:
driver: local
Nginx Configuration
docker/nginx/default.conf
:
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
}
Building and Running
Build Images
docker-compose build
Start Services
docker-compose up -d
Run Migrations
docker-compose exec app php glueful migrate:run
View Logs
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f app
docker-compose logs -f worker
Production Dockerfile
Dockerfile.prod
:
FROM php:8.2-fpm AS base
# Install dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd opcache \
&& pecl install redis \
&& docker-php-ext-enable redis
# Configure PHP
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.max_accelerated_files=20000" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
# Copy composer files
COPY composer.json composer.lock ./
# Install dependencies
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
# Copy application
COPY . .
# Generate optimized autoloader
RUN composer dump-autoload --optimize --classmap-authoritative
# Set permissions
RUN chown -R www-data:www-data /var/www \
&& chmod -R 755 /var/www \
&& chmod -R 775 /var/www/storage
EXPOSE 9000
CMD ["php-fpm"]
PostgreSQL users: add the PostgreSQL extension
# If you use PostgreSQL
RUN apt-get update && apt-get install -y libpq-dev \
&& docker-php-ext-install pdo_pgsql \
&& rm -rf /var/lib/apt/lists/*
Docker Compose for Production
docker-compose.prod.yml
:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
container_name: glueful-app
restart: always
environment:
- APP_ENV=production
- APP_DEBUG=false
env_file:
- .env.production
networks:
- glueful
nginx:
image: nginx:alpine
container_name: glueful-nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./public:/var/www/public:ro
- ./docker/nginx/prod.conf:/etc/nginx/conf.d/default.conf:ro
- ./ssl:/etc/nginx/ssl:ro
networks:
- glueful
db:
image: mysql:8.0
container_name: glueful-db
restart: always
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
volumes:
- dbdata:/var/lib/mysql
networks:
- glueful
redis:
image: redis:alpine
container_name: glueful-redis
restart: always
command: redis-server --appendonly yes
volumes:
- redisdata:/data
networks:
- glueful
worker:
build:
context: .
dockerfile: Dockerfile.prod
container_name: glueful-worker
restart: always
command: php glueful queue:work
environment:
- APP_ENV=production
env_file:
- .env.production
networks:
- glueful
deploy:
replicas: 4
scheduler:
build:
context: .
dockerfile: Dockerfile.prod
container_name: glueful-scheduler
restart: always
command: php glueful queue:scheduler work
environment:
- APP_ENV=production
env_file:
- .env.production
networks:
- glueful
networks:
glueful:
driver: bridge
volumes:
dbdata:
redisdata:
Multi-Stage Build
Optimized Dockerfile with multi-stage build:
# Build stage
FROM php:8.2-cli AS builder
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /build
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader
COPY . .
RUN composer dump-autoload --optimize --classmap-authoritative
# Production stage
FROM php:8.2-fpm
RUN apt-get update && apt-get install -y \
libpng-dev \
libonig-dev \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd opcache \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /build /var/www
WORKDIR /var/www
RUN chown -R www-data:www-data /var/www
EXPOSE 9000
CMD ["php-fpm"]
PostgreSQL users: add the PostgreSQL extension in the production stage
# In the production stage (after php:8.2-fpm)
RUN apt-get update && apt-get install -y libpq-dev \
&& docker-php-ext-install pdo_pgsql \
&& rm -rf /var/lib/apt/lists/*
Kubernetes Deployment
k8s/deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: glueful-app
spec:
replicas: 3
selector:
matchLabels:
app: glueful
template:
metadata:
labels:
app: glueful
spec:
containers:
- name: app
image: your-registry/glueful:latest
ports:
- containerPort: 9000 # php-fpm
env:
- name: APP_ENV
value: "production"
- name: DB_HOST
value: "mysql-service"
- name: REDIS_HOST
value: "redis-service"
envFrom:
- secretRef:
name: glueful-secrets
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
- name: public
mountPath: /var/www/public
volumes:
- name: nginx-conf
configMap:
name: glueful-nginx
- name: public
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: glueful-service
spec:
selector:
app: glueful
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
Nginx ConfigMap (minimal)
k8s/nginx-configmap.yaml
:
apiVersion: v1
kind: ConfigMap
metadata:
name: glueful-nginx
data:
default.conf: |
server {
listen 80;
server_name _;
# Minimal API-only config: route all to PHP-FPM in the same pod
location / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/public/index.php;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass 127.0.0.1:9000;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/public$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}
Notes:
- For static assets, mount your
public/
directory into the Nginx container or serve assets via a CDN/object storage. The minimal config above is API-focused.
Health Checks
Use Glueful's built-in endpoints (no custom route required):
- Liveness:
GET /healthz
- Overall:
GET /health
- Readiness:
GET /ready
(IP allowlist)
Docker Compose health check:
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Environment Variables
.env.production
:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://api.example.com
DB_DRIVER=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=production
DB_USERNAME=glueful
DB_PASSWORD=${DB_PASSWORD}
CACHE_DRIVER=redis
REDIS_HOST=redis
REDIS_PORT=6379
QUEUE_CONNECTION=redis
# Security keys
TOKEN_SALT=${TOKEN_SALT}
JWT_KEY=${JWT_KEY}
Best Practices
Use .dockerignore
.dockerignore
:
.git
.env
.env.*
!.env.example
node_modules
vendor
storage/logs/*
storage/cache/*
tests
.phpunit.cache
Multi-Container Deployment
Separate concerns:
- App container (PHP-FPM)
- Web server (Nginx)
- Database (MySQL/PostgreSQL)
- Cache (Redis)
- Worker (queue processing)
Volume Management
volumes:
# Named volumes for persistence
- dbdata:/var/lib/mysql
- redisdata:/data
# Bind mounts for development
- ./:/var/www
# Read-only mounts for production
- ./public:/var/www/public:ro
Deployment Commands
# Build and start
docker-compose -f docker-compose.prod.yml up -d --build
# Run migrations
docker-compose -f docker-compose.prod.yml exec app php glueful migrate:run
# Scale workers
docker-compose -f docker-compose.prod.yml up -d --scale worker=8
# (Optional) run dedicated scheduler service
docker-compose -f docker-compose.prod.yml up -d scheduler
# View logs
docker-compose -f docker-compose.prod.yml logs -f app
# Restart service
docker-compose -f docker-compose.prod.yml restart app
Troubleshooting
Container won't start?
- Check logs:
docker-compose logs app
- Verify environment variables
- Check file permissions
Database connection failed?
- Ensure database container is running
- Check
DB_HOST
matches service name - Verify credentials
Permission denied?
- Run:
docker-compose exec app chown -R www-data:www-data /var/www
Next Steps
- Production Setup - Server deployment
- Zero Downtime - Rolling updates
- Monitoring - Container monitoring