Skip to content

Docker Deployment

Deploy MarchProxy using Docker and Docker Compose for development, testing, and production environments.

Quick Start

Prerequisites

  • Docker 20.10+
  • Docker Compose 2.0+
  • 4GB RAM minimum
  • 20GB storage

Basic Deployment

  1. Clone and configure:

    git clone https://github.com/penguintechinc/marchproxy.git
    cd marchproxy
    cp .env.example .env
    # Edit .env with your settings
    

  2. Start services:

    docker-compose up -d
    

  3. Verify deployment:

    docker-compose ps
    curl http://localhost:8000/healthz
    

Production Deployment

Environment Configuration

Create a production environment file:

# .env.production
MARCHPROXY_ENV=production
DEBUG=false
LOG_LEVEL=info

# Strong passwords (generate secure values)
POSTGRES_PASSWORD=$(openssl rand -base64 32)
REDIS_PASSWORD=$(openssl rand -base64 32)
SECRET_KEY=$(openssl rand -base64 64)
GRAFANA_PASSWORD=$(openssl rand -base64 16)

# Database
DATABASE_URL=postgresql://marchproxy:${POSTGRES_PASSWORD}@postgres:5432/marchproxy

# Enterprise features
LICENSE_KEY=PENG-XXXX-XXXX-XXXX-XXXX-ABCD
CLUSTER_API_KEY=$(openssl rand -base64 32)

# Performance
ENABLE_XDP=true
WORKER_THREADS=4

Production Docker Compose

# docker-compose.prod.yml
version: '3.8'

services:
  manager:
    image: penguintech/marchproxy-manager:latest
    restart: always
    environment:
      - MARCHPROXY_ENV=production
      - DEBUG=false
    volumes:
      - manager_data:/app/data
      - ./certs:/app/certs:ro
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '1.0'
          memory: 1G
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3

  proxy:
    image: penguintech/marchproxy-proxy:latest
    restart: always
    privileged: true  # Required for eBPF/XDP
    network_mode: host  # Required for packet capture
    environment:
      - ENABLE_XDP=true
      - WORKER_THREADS=4
    volumes:
      - ./certs:/app/certs:ro
    deploy:
      resources:
        limits:
          cpus: '4.0'
          memory: 4G
        reservations:
          cpus: '2.0'
          memory: 2G
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3

  postgres:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_USER: marchproxy
      POSTGRES_DB: marchproxy
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./scripts/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M

  redis:
    image: redis:7-alpine
    restart: always
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

volumes:
  manager_data:
  postgres_data:
  redis_data:

SSL Certificate Setup

# Create certificates directory
mkdir -p certs

# Option 1: Use Let's Encrypt
certbot certonly --standalone \
  -d proxy.company.com \
  --cert-path ./certs/server.crt \
  --key-path ./certs/server.key

# Option 2: Use existing certificates
cp /path/to/your/certificate.crt certs/server.crt
cp /path/to/your/private.key certs/server.key

# Option 3: Generate self-signed (development only)
openssl req -x509 -newkey rsa:4096 \
  -keyout certs/server.key \
  -out certs/server.crt \
  -days 365 -nodes \
  -subj "/CN=proxy.company.com"

# Set proper permissions
chmod 600 certs/server.key
chmod 644 certs/server.crt
chown root:root certs/*

High Availability Setup

For HA deployment with multiple proxy instances:

# docker-compose.ha.yml
version: '3.8'

services:
  manager:
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.role == manager

  proxy:
    deploy:
      replicas: 3
      placement:
        max_replicas_per_node: 1

  postgres:
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.postgres == true

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - manager
      - proxy

Load Balancer Configuration

# nginx.conf for load balancing
upstream manager_backend {
    least_conn;
    server manager_1:8000;
    server manager_2:8000;
}

upstream proxy_backend {
    least_conn;
    server proxy_1:8080;
    server proxy_2:8080;
    server proxy_3:8080;
}

server {
    listen 80;
    listen 443 ssl;
    server_name proxy.company.com;

    ssl_certificate /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    location / {
        proxy_pass http://manager_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;
    }

    location /proxy/ {
        proxy_pass http://proxy_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Monitoring Setup

Integrated Monitoring Stack

# docker-compose.monitoring.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    restart: always
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'

  grafana:
    image: grafana/grafana:latest
    restart: always
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
      - ./grafana/datasources:/etc/grafana/provisioning/datasources:ro

  loki:
    image: grafana/loki:latest
    restart: always
    ports:
      - "3100:3100"
    volumes:
      - loki_data:/loki
      - ./loki.yml:/etc/loki/local-config.yaml:ro

  promtail:
    image: grafana/promtail:latest
    restart: always
    volumes:
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./promtail.yml:/etc/promtail/config.yml:ro

volumes:
  prometheus_data:
  grafana_data:
  loki_data:

Prometheus Configuration

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'marchproxy-manager'
    static_configs:
      - targets: ['manager:8000']
    metrics_path: '/metrics'

  - job_name: 'marchproxy-proxy'
    static_configs:
      - targets: ['proxy:8081']
    metrics_path: '/metrics'

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres_exporter:9187']

  - job_name: 'redis'
    static_configs:
      - targets: ['redis_exporter:9121']

Security Hardening

Container Security

# Security-hardened service configuration
services:
  manager:
    security_opt:
      - no-new-privileges:true
    user: "1000:1000"
    read_only: true
    tmpfs:
      - /tmp
      - /var/cache
    volumes:
      - manager_data:/app/data
      - ./certs:/app/certs:ro

  proxy:
    security_opt:
      - no-new-privileges:true
    cap_add:
      - NET_ADMIN      # Required for eBPF/XDP
      - SYS_ADMIN      # Required for eBPF loading
    cap_drop:
      - ALL

Network Security

# Network isolation
networks:
  frontend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24
  backend:
    driver: bridge
    internal: true
    ipam:
      config:
        - subnet: 172.21.0.0/24

services:
  manager:
    networks:
      - frontend
      - backend

  proxy:
    networks:
      - frontend

  postgres:
    networks:
      - backend

  redis:
    networks:
      - backend

Backup and Recovery

Database Backup

#!/bin/bash
# backup.sh - Database backup script

BACKUP_DIR="/backup/marchproxy"
DATE=$(date +%Y%m%d_%H%M%S)
CONTAINER_NAME="marchproxy_postgres_1"

mkdir -p ${BACKUP_DIR}

# Create database backup
docker exec ${CONTAINER_NAME} pg_dump -U marchproxy marchproxy | \
  gzip > ${BACKUP_DIR}/marchproxy_${DATE}.sql.gz

# Backup environment and compose files
cp .env ${BACKUP_DIR}/env_${DATE}
cp docker-compose.yml ${BACKUP_DIR}/compose_${DATE}.yml

# Clean old backups (keep 30 days)
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +30 -delete

echo "Backup completed: ${BACKUP_DIR}/marchproxy_${DATE}.sql.gz"

Volume Backup

#!/bin/bash
# volume-backup.sh - Volume backup script

docker run --rm \
  -v marchproxy_postgres_data:/source:ro \
  -v /backup:/backup \
  alpine tar czf /backup/postgres_data_$(date +%Y%m%d).tar.gz -C /source .

docker run --rm \
  -v marchproxy_manager_data:/source:ro \
  -v /backup:/backup \
  alpine tar czf /backup/manager_data_$(date +%Y%m%d).tar.gz -C /source .

Recovery Procedure

#!/bin/bash
# restore.sh - Database restore script

BACKUP_FILE="$1"
if [ -z "$BACKUP_FILE" ]; then
  echo "Usage: $0 <backup_file.sql.gz>"
  exit 1
fi

# Stop services
docker-compose down

# Restore database
docker-compose up -d postgres
sleep 10
gunzip -c ${BACKUP_FILE} | docker exec -i marchproxy_postgres_1 psql -U marchproxy marchproxy

# Start all services
docker-compose up -d

echo "Restore completed from: ${BACKUP_FILE}"

Performance Tuning

System Optimization

# /etc/sysctl.d/99-marchproxy.conf
# Network performance tuning
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 65536
net.core.wmem_default = 65536
net.netfilter.nf_conntrack_max = 1048576

# Memory optimization
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5

# Apply settings
sysctl -p /etc/sysctl.d/99-marchproxy.conf

Docker Optimization

# /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2",
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 65536,
      "Soft": 65536
    }
  }
}

# Restart Docker daemon
sudo systemctl restart docker

Troubleshooting

Common Deployment Issues

  1. Permission Errors:

    # Fix Docker permissions
    sudo usermod -aG docker $USER
    newgrp docker
    

  2. Port Conflicts:

    # Check port usage
    sudo netstat -tulpn | grep :8000
    # Change ports in docker-compose.yml
    

  3. Memory Issues:

    # Increase Docker memory
    # Edit Docker Desktop settings or daemon.json
    

  4. eBPF Failures:

    # Install kernel headers
    sudo apt install linux-headers-$(uname -r)
    

Log Analysis

# View service logs
docker-compose logs -f manager
docker-compose logs -f proxy

# Export logs
docker-compose logs --no-color > marchproxy-logs.txt

# Monitor resource usage
docker stats

Migration

Version Upgrades

# Backup before upgrade
./backup.sh

# Pull new images
docker-compose pull

# Update with zero downtime
docker-compose up -d --no-deps manager
docker-compose up -d --no-deps proxy

# Verify upgrade
curl http://localhost:8000/version
curl http://localhost:8080/version

Next: Kubernetes Deployment