Docker Deployment

docker-compose.yml

version: "3.8"

services:
  web:
    build: .
    command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4
    volumes:
      - .:/app
      - static_data:/app/staticfiles
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      - redis
      - db
    restart: always

  celery-worker:
    build: .
    command: celery -A config worker -l info
    volumes:
      - .:/app
    env_file:
      - .env
    depends_on:
      - redis
      - db
    restart: always

  celery-beat:
    build: .
    command: celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
    volumes:
      - .:/app
    env_file:
      - .env
    depends_on:
      - redis
      - db
    restart: always

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    restart: always

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: subscription_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
    ports:
      - "5432:5432"
    restart: always

volumes:
  postgres_data:
  static_data:

Dockerfile

FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn psycopg2-binary

# Copy project
COPY . .

# Collect static files
RUN python manage.py collectstatic --noinput 2>/dev/null || true

EXPOSE 8000

.env for Docker

DJANGO_SECRET_KEY=change-me-to-a-strong-secret-key
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com

DB_ENGINE=django.db.backends.postgresql
DB_NAME=subscription_db
DB_USER=postgres
DB_PASSWORD=
DB_HOST=db
DB_PORT=5432

CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0

MIDTRANS_SERVER_KEY=
MIDTRANS_CLIENT_KEY=
MIDTRANS_MERCHANT_ID=
MIDTRANS_IS_PRODUCTION=False
MIDTRANS_NOTIFICATION_URL=

Running

# Build and start
docker compose up -d --build

# Run migrations
docker compose exec web python manage.py migrate

# Setup periodic tasks
docker compose exec web python manage.py setup_periodic_tasks

# Seed sample plans
docker compose exec web python manage.py seed_plans

# Create superuser
docker compose exec web python manage.py createsuperuser

# View logs
docker compose logs -f web celery-worker celery-beat

Stopping

docker compose down          # Stop containers
docker compose down -v       # Stop and remove volumes (destroys data)