Build production-ready Docker configurations for full-stack applications with monitoring, load balancing, and multi-environment support. Use when dockerizing applications, creating Dockerfiles, setting up docker-compose, adding Nginx reverse proxy, configuring Prometheus and Grafana, or deploying containerized applications. Covers FastAPI, Next.js, databases, caches, Celery workers, and production deployments with security hardening.
/plugin marketplace add adelabdelgawad/full-stack/plugin install docker-stack@full-stackThis skill is limited to using the following tools:
PATTERNS.mdREFERENCE.mdThis skill teaches how to build production-ready Docker configurations following industry best practices for security, performance, and reliability.
Architecture Covered:
Frontend (Next.js) ─┐
├─> Nginx Load Balancer ─> Internet
Backend (FastAPI) ──┘
│
├─> Database (MariaDB/PostgreSQL)
├─> Cache (Redis)
├─> Celery Workers
└─> Monitoring (Prometheus + Grafana)
CRITICAL RULES:
Activate when request involves:
| Service | Purpose | Base Image | Key Features |
|---|---|---|---|
| Backend | FastAPI API | python:3.12-alpine | Multi-stage, UV, Gunicorn |
| Frontend | Next.js App | node:20-alpine | Multi-stage, standalone output |
| Database | MariaDB | mariadb:11.2 | Persistent volumes, health checks |
| Cache | Redis | redis:7.2-alpine | AOF persistence, LRU policy |
| Worker | Celery | Same as backend | Gevent pool, auto-retry |
| Beat | Scheduler | Same as backend | Single instance only |
| Nginx | Load Balancer | nginx:1.25-alpine | Reverse proxy, SSL ready |
| Prometheus | Metrics | prom/prometheus | 15d retention, alerts |
| Grafana | Dashboards | grafana/grafana | Provisioned dashboards |
project/
├── docker/ # Production configs
│ ├── docker-compose.prod.yml # Production compose
│ ├── env/ # Environment templates
│ │ ├── .env.example.backend
│ │ ├── .env.example.database
│ │ ├── .env.example.redis
│ │ ├── .env.example.frontend
│ │ └── .env.example.grafana
│ ├── nginx/
│ │ ├── Dockerfile
│ │ └── nginx.conf
│ └── monitoring/
│ ├── prometheus/
│ │ ├── prometheus.yml
│ │ ├── alerts.yml
│ │ └── recording_rules.yml
│ └── grafana/
│ └── provisioning/
├── docker-compose.yml # Development compose
├── src/
│ ├── backend/
│ │ ├── Dockerfile
│ │ └── .dockerignore
│ └── frontend/
│ ├── Dockerfile
│ └── .dockerignore
└── .dockerignore
Backend (Python/FastAPI with UV):
# Stage 1: Builder
FROM ghcr.io/astral-sh/uv:python3.12-alpine AS builder
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
# Install build dependencies
RUN apk add --no-cache gcc musl-dev libffi-dev openssl-dev mariadb-dev
# Install dependencies first (better caching)
COPY pyproject.toml uv.lock ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-install-project --no-editable
# Copy source and install project
COPY . .
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-editable
# Stage 2: Runtime
FROM python:3.12-alpine AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH=/app
# Install ONLY runtime dependencies
RUN apk add --no-cache curl ca-certificates libffi openssl mariadb-connector-c
# Create non-root user
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appuser /app/.venv /app/.venv
COPY --from=builder --chown=appuser:appuser /app /app
ENV PATH="/app/.venv/bin:$PATH"
USER appuser
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1
EXPOSE 8000
CMD ["gunicorn", "app:app", \
"--workers", "4", \
"--worker-class", "uvicorn.workers.UvicornWorker", \
"--bind", "0.0.0.0:8000"]
Frontend (Next.js):
# Stage 1: Dependencies
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Stage 3: Runtime
FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
RUN apk add --no-cache dumb-init
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" || exit 1
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]
Backend (.dockerignore):
# Virtual environments
.venv/
venv/
ENV/
# Python cache
__pycache__/
*.py[cod]
*.so
# Tests
tests/
.pytest_cache/
.coverage
# IDE
.vscode/
.idea/
# Environment files (mounted at runtime)
.env
.env.*
!.env.example
# Git
.git/
# Documentation
*.md
docs/
# Logs
logs/
*.log
services:
database:
image: mariadb:11.2
container_name: ${PROJECT_NAME:-app}-db
restart: unless-stopped
ports:
- "3306:3306"
env_file:
- ./docker/env/.env.database
volumes:
- mariadb_data:/var/lib/mysql
networks:
- app-network
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
command: >
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--max_connections=200
redis:
image: redis:7.2-alpine
container_name: ${PROJECT_NAME:-app}-redis
restart: unless-stopped
command: >
redis-server
--appendonly yes
--maxmemory 512mb
--maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
backend:
build:
context: ./src/backend
dockerfile: Dockerfile
container_name: ${PROJECT_NAME:-app}-backend
restart: unless-stopped
ports:
- "8000:8000"
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
env_file:
- ./docker/env/.env.backend
volumes:
- backend_logs:/app/logs
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
frontend:
build:
context: ./src/frontend
dockerfile: Dockerfile
container_name: ${PROJECT_NAME:-app}-frontend
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- ./docker/env/.env.frontend
networks:
- app-network
volumes:
mariadb_data:
driver: local
redis_data:
driver: local
backend_logs:
driver: local
networks:
app-network:
driver: bridge
Key differences from development:
services:
# Multiple backend instances
backend-1:
build: ./src/backend
environment:
- INSTANCE_ID=backend-1
deploy:
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
backend-2:
build: ./src/backend
environment:
- INSTANCE_ID=backend-2
deploy:
resources:
limits:
cpus: '1'
memory: 1G
# Nginx load balancer
nginx:
build: ./docker/nginx
ports:
- "80:80"
- "443:443"
depends_on:
- backend-1
- backend-2
volumes:
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
upstream backend {
least_conn;
server backend-1:8000 max_fails=3 fail_timeout=30s;
server backend-2:8000 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name _;
location /api/ {
proxy_pass http://backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /health {
access_log off;
return 200 "healthy\n";
}
}
.env.example.backend:
# Database
DB_USER=app_user
DB_PASSWORD=changeme_db_password
DB_SERVER=database
DB_NAME=app_db
DB_PORT=3306
# Redis
REDIS_URL=redis://redis:6379/0
# JWT
JWT_SECRET_KEY=changeme_jwt_secret_key_min_32_chars
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=30
# Application
ENVIRONMENT=production
LOG_LEVEL=INFO
ALLOWED_ORIGINS=http://localhost,https://example.com
# Celery
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/1
# Create user
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
# Set ownership
COPY --from=builder --chown=appuser:appuser /app /app
# Switch to user
USER appuser
# ❌ WRONG - Hardcoded secrets
ENV DB_PASSWORD=mypassword
# ✓ CORRECT - Use env_file in compose
# docker-compose.yml
env_file:
- ./docker/env/.env.backend
# ✓ Alpine (5 MB base)
FROM python:3.12-alpine
# ❌ Full Debian (80+ MB base)
FROM python:3.12
# docker-compose.yml
services:
app:
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
- /app/logs
# Dependencies first (rarely change)
COPY pyproject.toml uv.lock ./
RUN uv sync
# Source code last (changes often)
COPY . .
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked
# Enable BuildKit for faster builds
export DOCKER_BUILDKIT=1
docker-compose build
# prometheus.yml
scrape_configs:
- job_name: 'backend'
static_configs:
- targets: ['backend-1:8000', 'backend-2:8000']
metrics_path: '/metrics'
scrape_interval: 15s
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'mariadb-exporter'
static_configs:
- targets: ['mariadb-exporter:9104']
Before completing Docker setup:
# Check context size
du -sh .
# Optimize .dockerignore
echo "node_modules/" >> .dockerignore
echo ".venv/" >> .dockerignore
echo ".git/" >> .dockerignore
# Enable BuildKit
export DOCKER_BUILDKIT=1
# Use cache mounts
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# Check logs
docker-compose logs <service>
# Increase health check start_period
healthcheck:
start_period: 60s # Give more time to start
# Use service name (not localhost)
DB_SERVER=database # ✓ Correct
DB_SERVER=localhost # ❌ Wrong in Docker
# Ensure depends_on with health check
depends_on:
database:
condition: service_healthy
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.