Repository navigation
Docker Production v2
This page details the Docker-based Production v2 setup, focusing on its architecture, networking, orchestration, and deployment.
- Overview
- Network Diagram
-
Docker Compose Services
3.1 The Wall API
3.2 Redis
3.3 PostgreSQL
3.4 Celery Worker 1
3.5 Celery Worker 2
3.6 Celery Beat -
Dockerfile
4.1 Base Image
4.2 Application Image
4.3 Celery Lightweight Image -
Image Lifecycle
5.1 Image Build and Push to GHCR
5.2 Pulling Images and Deployment
The prod_v2 setup introduces Docker Swarm for orchestration, enabling secure secrets management and consistent service deployment while maintaining compatibility with future scaling.
-
Although Production v2 runs on a single node, Swarm is used primarily to:
✔ Leverage Docker Secrets - Ensures sensitive credentials are securely stored and accessed without being exposed in environment variables.
✔ Maintain a scalable architecture - Allows for seamless expansion to multiple nodes if needed, without major reconfiguration.
✔ Preserve the Docker Compose workflow -
docker-compose-prod-v2.ymlis derived fromdocker-compose-prod-v1.yml, adapting the existing Compose-based structure for Swarm. This ensures a familiar configuration while integrating Swarm’s secrets management and orchestration features.✔ Support existing volume-based persistence - Since all services run on a single node, local Docker volumes remain effective for data persistence.
This diagram represents the established network architecture used across the prod_v1 and demo deployments, which also applies to prod_v2. This setup ensures a stable foundation while supporting enhancements for orchestration and security.

🗒️ For a comparison with existing setups, see the Docker - Production v1 and Demo page.
The prod_v2 setup defines its service stack in docker-compose-prod-v2.yml, which is deployed using Docker Swarm.
| Service name | Image | Ports | Named Volumes | Secrets Used |
|---|---|---|---|---|
| the_wall_api | ghcr.io/alexandar83/ the-wall-api:latest |
8000:8000 |
shared_sim_logs:/app/logs
|
✅ |
| redis | redis:7.4.1 | 6379:6379 |
the_wall_api_redis_data_prod:/data
|
✅ |
| postgres | postgres:15.3 | 5432:5432 |
the_wall_api_postgres_data_prod:/var/lib/postgresql/data
|
✅ |
| celery_worker_1 | ghcr.io/alexandar83/ the-wall-api:latest |
- |
shared_sim_logs:/app/logs
|
✅ |
| celery_worker_2 | ghcr.io/alexandar83/ the-wall-api:latest |
- |
shared_sim_logs:/app/logs
|
✅ |
| celery_beat | ghcr.io/alexandar83/ celery-lightweight:latest |
- |
celery_beat_schedule:/var/lib/celery/data
|
✅ |
🗒️ In Docker Swarm, services do not have static container names. Instead, they follow the format <service_name>.<task_id>.<node_id>, for example:
• the_wall_api.1.sj9f8g2h7k3l
• celery_worker_1.2.jd83kd9s8g5k
✔ The shared_sim_logs volume enables unified log management, allowing the API and Celery workers to store, update, and access logs in a shared location, just as in previous deployment modes.
Serves as the primary application service.
-
Starts with the following command:
python /app/config/scripts/docker/wait_for_postgres.py && \ python manage.py makemigrations the_wall_api && \ python manage.py migrate && \ exec gunicorn --bind 0.0.0.0:8000 config.wsgi
-
Command Breakdown:
- wait_for_postgres.py: Script to wait for the full initialization of the PostgreSQL service.
-
makemigrations: Detects and prepares database schema changes. -
migrate: Executes pending database migrations to update the schema before starting the server. -
gunicorn: Serves as the production WSGI server, handling requests and binding to all interfaces.
✔ Docker Swarm does not support the
depends_ondirective. Thewait_for_postgres.pyscript ensures PostgreSQL is fully ready before applying migrations.✔
gunicornenhances request handling efficiency in production, ensuring optimal performance under load.
-
-
Uses
the_wall_api_prod.envto load environment variables. -
Sets
PROJECT_MODE=prod_v2to indicate the production version of the environment. -
Mounts the Redis password, PostgreSQL credentials, and the app's secret key in
/run/secrets/.✔ The secrets are read by the application at runtime, ensuring they are never stored in environment variables or image layers. This enhances security by keeping sensitive data outside the container’s build process and preventing accidental exposure.
Handles data caching and functions as a message broker for Celery task queues.
- Uses a Docker-managed volume (redis_data_prod) to persist data across restarts.
- Mounts
config/envs/common/redis.confto/usr/local/etc/redis/redis.confinside the container. - The start command reads the dedicated password from the Docker secret at runtime:
redis-server /usr/local/etc/redis/redis.conf --requirepass "$$(cat /run/secrets/redis_password)"
✔ Mounting redis.conf enables dynamic configuration, allowing environment-specific adjustments without modifying the base image or requiring container rebuilds.
💡 For a detailed breakdown of redis.conf settings, refer to the Redis configuration file section.
Manages persistent database storage for the API.
- Utilizes a Docker-managed volume (postgres_data_prod) to retain data persistently across restarts.
-
Credentials are read from Docker Secrets, which store:
-
POSTGRES_DB- Database name -
POSTGRES_PASSWORD- Database password -
POSTGRES_USER- Database username
-
✔ Docker Secrets keep credentials separate from environment variables and image layers, enhancing security and preventing accidental exposure.
Processes sequential Celery tasks.
- Starts using the
start_celery_computation_worker.shscript, which:- Runs the Celery worker without a
--concurrencyflag, allowing automatic adjustment based on system resources.- If
CONCURRENT_SIMULATION_MODEis set tomultiprocessing_v1,2, or 3, applies explicit concurrency settings (refer to script for details).
- If
- Assigns the worker to the
concurrent_tasksqueue.
- Runs the Celery worker without a
-
Configurations identical to The Wall API:
- Uses the same image (
the_wall_api:latest). - Environment variables are loaded from
the_wall_api_prod.env. PROJECT_MODE=prod_v2- Uses Swarm secrets for credentials and sensitive data.
- Uses the same image (
Processes sequential Celery tasks.
- Worker 2 follows the same configuration structure as Celery Worker 1, differing only in its start command:
celery -A config worker --loglevel=info --queues=sequential_tasks --concurrency=1- Command Breakdown:
-
celery -A config worker: Launches a Celery worker, using the Celery app instance fromconfig/celery.py. -
--loglevel=info: Sets the worker’s logging level toinfo. -
--queues=sequential_tasks: Assigns the worker to thesequential_tasksqueue. -
--concurrency=1: Enforces single-task execution to maintain sequential processing.
-
- Command Breakdown:
Schedules recurring Celery tasks, including database cleanup, file archiving, and archive retention.
- Uses a lightweight image based on
Dockerfile.prod. - Start command:
python /app/config/scripts/docker/wait_for_celery_worker.py && \ celery -A config beat --loglevel=info -s /var/lib/celery/data/celerybeat-schedule
-
wait_for_celery_worker.py: Waits for both Celery workers to be ready.
✔ Docker Swarm does not provide built-in service dependencies. The script ensures both Celery workers are running before starting Celery Beat. -
-A config beat: Starts Celery Beat for task scheduling. -
-s /var/lib/celery/data/celerybeat-schedule: Defines the schedule file’s location within the container.
-
- Persists the task schedule in the celery_beat_schedule volume, preventing loss across restarts.
- Applies a lightweight Celery configuration (
LIGHT_CELERY_CONFIG=True), reducing overhead by skipping Django initialization.
-
Configurations identical to Celery Worker 1:
- Environment variables from
the_wall_api_prod.env. PROJECT_MODE=prod_v2- Swarm secrets for credentials and sensitive data.
- Environment variables from
✔ The lightweight image minimizes overhead by excluding unnecessary dependencies.
Dockerfile.prod defines a multi-stage build used in Docker Swarm
to create images for the API, Celery Worker 1, Celery Worker 2, and Celery Beat.
Establishes the base environment for subsequent build stages.
-
Configures essential environment variables:
-
PYTHONDONTWRITEBYTECODE=1(prevents .pyc file generation). -
PYTHONUNBUFFERED=1(ensures real-time logging output without buffering). -
PYTHON_VERSION=3.11.9(acts as a reference for scripts and automation but does not impact the installed version). -
LOGS_DIR_NAME=logs(standardizes the log folder name across all build stages, ensuring consistency for the application and Celery workers).
-
-
Sets
/appas the working directory. -
Creates a non-root user for security:
RUN adduser -u 5678 --disabled-password --gecos "" appuser
- Disables password authentication to skip interactive logins.
- Leaves user details empty to avoid prompts.
- Assigns the username 'appuser'.
✔ Using a minimal slim-bullseye base image optimizes container size and reduces security risks.
✔ Running as a non-root user enhances security by restricting elevated privileges within the container.
Serves as the base for Celery Worker 1, Celery Worker 2, and the API container, ensuring the required application code is available for each service.
-
Installs system dependencies
gccandlibpq-dev(needed to compile thepsycopg2PostgreSQL adapter from source). -
Copies
requirements.txtand installs Python dependencies withpip. -
Installs
gunicornseparately to serve the application as a WSGI server.
🗒️gunicornis not included inrequirements.txtsince it is not an application dependency. -
Copies the application to
/appinside the container.
🗒️ .dockerignore filters copied files to reduce image size and optimize the build. -
Configures log folder permissions:
- Creates
/app/${LOGS_DIR_NAME}to avoid permission issues for Celery workers. - Assigns ownership to the non-root user (
appuser).
- Creates
-
Runs all subsequent processes as
appuser.
✔ Reduces image size by:
• Removing package manager caches after installing PostgreSQL dependencies (rm -rf /var/lib/apt/lists/)
• Disabling package caching during Python dependency installation (--no-cache-dir)
Used by the Celery Beat service.
-
Installs only the necessary Celery and Redis dependencies from
requirements.txtusing:RUN grep -E '^celery==|^redis==' /app/requirements.txt > filtered-requirements.txt RUN pip install --no-cache-dir -r filtered-requirements.txt
✔ The extraction command ensures that the exact versions frozen in
requirements.txtare used, maintaining consistency across deployments.
-
Copies the application to
/app.
🗒️ File selection is controlled with .dockerignore. -
Configures Celery Beat schedule and log storage:
- Creates
/var/lib/celery/datato persist the task schedule. - Sets up the logs folder and assigns ownership to
appuser.
- Creates
✔ The lightweight design reduces the package footprint, making the container smaller and improving build speed.
The prod_v2 environment utilizes a structured workflow for building, pushing, and deploying Docker images. A full application image runs the API and Celery workers, while a lightweight Celery image is streamlined for Celery Beat. Both images are versioned and stored in GHCR for centralized management and deployment.
📝 All commands in this section should be run from the application's root folder.
Full Image
docker build --no-cache \
--target the_wall_api_image \
-t ghcr.io/alexandar83/the-wall-api:v2 \
-t ghcr.io/alexandar83/the-wall-api:latest \
-f ./config/docker/Dockerfile.prod \
.
docker push ghcr.io/alexandar83/the-wall-api --all-tags- Commands Breakdown
-
--no-cache: Forces a fresh build by skipping cached layers. -
--target the_wall_api_image: Defines the multi-stage build target for the API and Celery workers image. -
-t ghcr.io/alexandar83/the-wall-api:v2 -t ghcr.io/alexandar83/the-wall-api:latest: Assigns two tags to the built image:v2andlatest. -
-f ./config/docker/Dockerfile.prod: Specifies the Dockerfile location for the build. -
.: Sets the current directory as the build context. -
docker push ghcr.io/alexandar83/the-wall-api --all-tags: Pushes all image tags to GHCR.
-
Lightweight Image
docker build --no-cache \
--target celery_lightweight_image \
-t ghcr.io/alexandar83/celery-lightweight:v2 \
-t ghcr.io/alexandar83/celery-lightweight:latest \
-f ./config/docker/Dockerfile.prod \
.
docker push ghcr.io/alexandar83/celery-lightweight --all-tagsdocker stack deploy -c config/docker/docker-compose-prod-v2.yml prod_stack --detach=true-
Command Breakdown:
-
docker stack deploy– Deploys a new stack in Docker Swarm. -
-c config/docker/docker-compose-prod-v2.yml– Specifies the path to the Compose file used for deployment. -
prod_stack– Defines the stack name, grouping all services under this identifier. -
--detach=true– Runs the deployment in the background, allowing the console shell in which the command is executed to be used for other tasks.
-
💡 For detailed environment installation instructions, see the Production v2 setup guides for Windows and Linux (WSL2).
Next: DRF Spectacular
Back to: Docker - Production v1 and Demo
Return to Top
DTS Index
Home Page
📌 Notable Technical Sections
👤 Author
LinkedIn: Aleksandar Dimitrov
Yahoo: sasho_1983@yahoo.com