Docker Production Deployment
This guide covers deploying AutoCom using Docker Compose with production-optimized configurations.
Prerequisites
- Docker 24.0+
- Docker Compose 2.0+
- At least 4GB RAM
- 20GB available disk space
Production Dockerfiles
AutoCom uses multi-stage Dockerfiles optimized for production for both the backend and frontend.
Backend Dockerfile (backend/Dockerfile.prod)
# Stage 1: Dependencies - Install Composer packages
FROM composer:latest AS composer-deps
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-scripts \
--no-autoloader \
--no-interaction \
--prefer-dist \
--ignore-platform-reqs
# Stage 2: Base - Build PHP extensions
FROM php:8.4-fpm-alpine AS base
# Install extensions: pdo_pgsql, redis, opcache, etc.
# Copy optimized PHP configuration
# Generate optimized autoloader
# Stage 3: Production - Minimal runtime
FROM php:8.4-fpm-alpine AS production
# Copy only what's needed for runtime
# Set proper permissions
Frontend Dockerfile (frontend/Dockerfile.prod)
The frontend uses Next.js standalone output mode, producing a self-contained Node.js server with only the required dependencies:
# syntax=docker/dockerfile:1.6
# Stage 1: Dependencies — cached unless package.json/bun.lock change
FROM oven/bun:1-alpine AS deps
COPY frontend/package.json frontend/bun.lock ./
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install --frozen-lockfile
# Stage 2: Build — config files before source for layer caching
FROM oven/bun:1-alpine AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY frontend/next.config.js frontend/tsconfig.json ... # Config files first
COPY modules/ ../modules/ # Module frontend code
COPY frontend/app frontend/components frontend/lib ... # Source code last
RUN --mount=type=cache,target=/app/.next/cache \
bun run build
# Stage 3: Runner — minimal production image
# Standalone server only needs Node, so we drop bun for the runtime image.
FROM node:20-alpine AS runner
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
CMD ["node", "server.js"]
The final frontend image is approximately 280MB and runs via node server.js (no bun/npm at runtime, only the bundled node_modules required by Next.js standalone).
Key Optimizations
| Optimization | Benefit |
|---|---|
| Multi-stage build (backend) | Smaller final image (~150MB) |
| Multi-stage build (frontend) | Standalone output (~280MB image) |
| Alpine base | Minimal attack surface |
| OPcache precompilation | Faster PHP startup time |
| Route/View caching | Reduced runtime overhead |
| Layer caching (frontend) | Config files copied before source — bun install cached on code-only changes |
| BuildKit cache mounts | /root/.bun/install/cache + .next/cache survive between rebuilds |
| Standalone output | node server.js — no bun/npm runtime, only required node_modules bundled |
Incremental Rebuild Times
| Change Type | Rebuild Time | Notes |
|---|---|---|
| Code-only (frontend) | ~30–40s | install layer cached, only bun run build re-runs |
| New dependency | ~50–60s | install re-runs but the bun cache mount keeps deps warm |
| Backend PHP change | ~30s | Composer deps cached, only app code changes |
| SDUI module (backend only) | ~30s | Frontend image not rebuilt — see SDUI Deployment |
Building Production Images
# Build all production images
docker compose -f docker-compose.prod.yml build
# Build specific service
docker compose -f docker-compose.prod.yml build app
# Build without cache (for clean rebuild)
docker compose -f docker-compose.prod.yml build --no-cache
Docker Compose Configuration
Environment Variables
Create a .env file in the project root:
# Application
APP_NAME=AutoCom
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com
# Database
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=autocom
DB_USERNAME=autocom
DB_PASSWORD=your-secure-password
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password
# Cache & Queue
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
Production Compose File
The docker-compose.prod.yml includes:
services:
app:
build:
context: ./backend
dockerfile: Dockerfile.prod
environment:
- APP_ENV=production
depends_on:
- postgres
- redis
nginx:
image: nginx:alpine
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "80:80"
- "443:443"
postgres:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: autocom
POSTGRES_USER: autocom
POSTGRES_PASSWORD: ${DB_PASSWORD}
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
horizon:
build:
context: ./backend
dockerfile: Dockerfile.prod
command: php artisan horizon
depends_on:
- app
- redis
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
environment:
- NEXT_PUBLIC_API_URL=${APP_URL}/api/v1
volumes:
postgres_data:
redis_data:
Starting Production Services
# Start all services in detached mode
docker compose -f docker-compose.prod.yml up -d
# View logs
docker compose -f docker-compose.prod.yml logs -f
# Check service status
docker compose -f docker-compose.prod.yml ps
Running Migrations
# Run database migrations
docker compose -f docker-compose.prod.yml exec app php artisan migrate --force
# Seed initial data (if needed)
docker compose -f docker-compose.prod.yml exec app php artisan db:seed --force
Health Checks
The production setup includes health checks for all services:
PHP-FPM Health Check
# Check PHP-FPM ping endpoint
docker compose exec app /usr/local/bin/health-check.sh
The health check script uses FastCGI to ping PHP-FPM:
#!/bin/sh
SCRIPT_NAME=/fpm-ping \
SCRIPT_FILENAME=/fpm-ping \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect 127.0.0.1:9000 | grep -q pong
Nginx Health Check
# Check nginx health endpoint
curl http://localhost/health
SSL/TLS Configuration
For production, configure SSL using Let's Encrypt:
# Add to docker-compose.prod.yml
services:
nginx:
volumes:
- ./certbot/conf:/etc/letsencrypt:ro
- ./certbot/www:/var/www/certbot:ro
certbot:
image: certbot/certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
command: certonly --webroot -w /var/www/certbot -d your-domain.com
Backup Configuration
Database Backup
# Create backup
docker compose exec postgres pg_dump -U autocom autocom > backup.sql
# Restore backup
docker compose exec -T postgres psql -U autocom autocom < backup.sql
Redis Backup
# Trigger Redis save
docker compose exec redis redis-cli -a ${REDIS_PASSWORD} BGSAVE
# Copy RDB file
docker cp $(docker compose ps -q redis):/data/dump.rdb ./redis-backup.rdb
Monitoring
View Resource Usage
# Check container stats
docker stats
# Check specific service
docker compose -f docker-compose.prod.yml top app
Log Management
# View all logs
docker compose -f docker-compose.prod.yml logs -f
# View specific service logs
docker compose -f docker-compose.prod.yml logs -f app
# Tail last 100 lines
docker compose -f docker-compose.prod.yml logs --tail=100 app
Troubleshooting
Common Issues
Container won't start:
# Check logs for errors
docker compose -f docker-compose.prod.yml logs app
# Verify environment variables
docker compose -f docker-compose.prod.yml config
Database connection issues:
# Verify postgres is running
docker compose exec postgres pg_isready
# Check connection from app
docker compose exec app php artisan db:monitor
Permission errors:
# Fix storage permissions
docker compose exec app chmod -R 755 storage bootstrap/cache
docker compose exec app chown -R www-data:www-data storage bootstrap/cache
SDUI Module Deployment
Modules that use the SDUI system define their screens as backend JSON. Because the web and mobile renderers fetch screen definitions from the API at runtime, SDUI modules do not require a frontend rebuild. Only the backend image needs updating.
When You Need to Rebuild
| Change | Backend rebuild | Frontend rebuild |
|---|---|---|
| New SDUI module (backend screen providers) | Yes | No |
| Update SDUI screen definition | Yes | No |
| New frontend page (non-SDUI) | No | Yes |
| New shadcn component or UI change | No | Yes |
| Backend API change | Yes | No |
Module Deployment Workflow
Adding a new SDUI module:
# 1. Rebuild backend only (includes new screen provider PHP classes)
docker compose -f docker-compose.prod.yml build app
# 2. Restart the backend
docker compose -f docker-compose.prod.yml up -d app
# 3. Sync module metadata to the database
docker compose -f docker-compose.prod.yml exec app php artisan module:sync
The new module's screens are immediately accessible at /m/{module}/{page} on the web and in the mobile app sidebar — no frontend image rebuild needed.
Updating an existing module's screens:
# Same as above — rebuild backend, restart, sync
docker compose -f docker-compose.prod.yml build app
docker compose -f docker-compose.prod.yml up -d app
docker compose -f docker-compose.prod.yml exec app php artisan module:sync
Full stack rebuild (frontend + backend changes):
# Build both images
docker compose -f docker-compose.prod.yml build app frontend
# Restart both services
docker compose -f docker-compose.prod.yml up -d app frontend
# Sync modules
docker compose -f docker-compose.prod.yml exec app php artisan module:sync
Important: Backend code is baked into the Docker image (not volume-mounted in production). You must rebuild the
appimage after any PHP changes — including new screen providers, route changes, or migration files.
Next Steps
- Configure PHP Optimization for maximum performance
- Deploy to Kubernetes for scalability
- Set up Autoscaling for handling traffic spikes