Installation

Full reference for the Docker Compose deployment, including reverse proxy and TLS.

System requirements

ResourceMinimumRecommended
CPU2 cores4+ cores
RAM2 GB4–8 GB
Disk10 GB50 GB+ (depends on retention)
OSAny Linux with Docker 20.10+Ubuntu LTS, Debian stable, RHEL 8+

The compose file

Four services: app, executor, mysql, clickhouse. Data is persisted in named Docker volumes; both databases include healthchecks — the app waits for them before starting.

docker-compose.yml
Download
services:
  app:
    image: ghcr.io/mlab-sh/ir.mlab.sh:latest
    ports:
      - "${APP_PORT:-8080}:8080"
    depends_on:
      mysql:      { condition: service_healthy }
      clickhouse: { condition: service_healthy }
    env_file: .env
    environment:
      - DB_HOST=mysql
      - CH_HOST=clickhouse
      - EXECUTOR_URL=http://executor:8090
    volumes:
      - uploads:/app_mlab_sh/uploads
      - logs:/app_mlab_sh/logs
    restart: unless-stopped

  executor:
    image: ghcr.io/mlab-sh/ir.mlab.sh-executor:latest
    depends_on:
      clickhouse: { condition: service_healthy }
    env_file: .env
    restart: unless-stopped

  mysql:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: mlabir
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
    restart: unless-stopped

  clickhouse:
    image: clickhouse/clickhouse-server:latest
    environment:
      CLICKHOUSE_USER: ${CH_USERNAME}
      CLICKHOUSE_PASSWORD: ${CH_PASSWORD}
      CLICKHOUSE_DB: mlabir
    volumes:
      - clickhouse_data:/var/lib/clickhouse
    healthcheck:
      test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
      interval: 5s
    restart: unless-stopped

volumes:
  mysql_data:
  clickhouse_data:
  uploads:
  logs:

Reverse proxy & TLS

Don't expose app directly. Put nginx, Caddy or Traefik in front for TLS termination, HTTP/2 and request limits.

Caddy

Caddyfile
ir.example.com {
    reverse_proxy localhost:8080
    encode gzip
}

nginx

/etc/nginx/sites-available/ir.conf
server {
    listen 443 ssl http2;
    server_name ir.example.com;
    ssl_certificate     /etc/letsencrypt/live/ir.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ir.example.com/privkey.pem;
    client_max_body_size 50m;

    location / {
        proxy_pass         http://127.0.0.1:8080;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}
Trusted proxy. Set TRUSTED_PROXY=127.0.0.1 (or your proxy IP) in .env so the app honors X-Forwarded-* headers for client IP logging.

Backups

Both database volumes are mountable. The recommended backup is volume-level snapshots plus a logical dump:

terminal
docker exec mysql mysqldump -uroot -p$DB_ROOT_PASSWORD mlabir | gzip > mlabir-$(date +%F).sql.gz
docker exec clickhouse clickhouse-client --query="BACKUP DATABASE mlabir TO Disk('backups','mlabir-$(date +%F).zip')"