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 Dockerfile

AutoCom uses a multi-stage Dockerfile optimized for production:

# 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

Key Optimizations

Optimization Benefit
Multi-stage build Smaller final image (~150MB)
Alpine base Minimal attack surface
OPcache precompilation Faster startup time
Route/View caching Reduced runtime overhead

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

Next Steps