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 register size)
OSAny Linux with Docker 20.10+Ubuntu LTS, Debian stable, RHEL 8+

The compose file

Three services: app (Rust / Actix-web 4), mysql (business data) and clickhouse (analytics & activity). Data is persisted in named Docker volumes; both databases include healthchecks — the app waits for them before starting. The image is pulled from GHCR.

docker-compose.yml
Download
services:
  app:
    image: ghcr.io/mlab-sh/tprm.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
    volumes:
      - uploads:/app_mlab_sh/uploads
      - logs:/app_mlab_sh/logs
    restart: unless-stopped

  mysql:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: mlabtprm
      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: mlabtprm
    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:

Ports

ServicePortExposed?
app8080Yes — behind your reverse proxy
mysql3306No — internal network only
clickhouse8123 / 9000No — internal network only

Pulling the image

Images are published to GitHub Container Registry via GitHub Actions. No authentication is required for the public image:

terminal
docker pull ghcr.io/mlab-sh/tprm.mlab.sh:latest

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
tprm.example.com {
    reverse_proxy localhost:8080
    encode gzip
}

nginx

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

    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 mlabtprm | gzip > mlabtprm-$(date +%F).sql.gz
docker exec clickhouse clickhouse-client --query="BACKUP DATABASE mlabtprm TO Disk('backups','mlabtprm-$(date +%F).zip')"