Skip to content

Traefik with Watchtower Deployment

This is the most straight forward way to host chithi instances.

To host chithi, you need 3 parts.

  1. A S3 compatible storage instance
  2. A Postgres Database instance
  3. A Redis Instance

Docker Compose

Here is a ready to use docker file that can be used to deploy your site:

services:
    traefik:
        image: traefik:latest
        container_name: traefik
        restart: unless-stopped
        ports:
            - '80:80'
            - '443:443/tcp'
            - '443:443/udp'
        command:
            - '--providers.docker=true'
            - '--providers.docker.exposedbydefault=false'
            - '--entrypoints.web.address=:80'
            - '--entrypoints.websecure.address=:443'

            # Enable dashboard
            - '--api.dashboard=true'
            - '--api.insecure=true'

            # Enable http/3
            - '--entrypoints.websecure.http3=true'

            # Experimental FastProxy
            - '--experimental.fastProxy'

            # Info logs
            - '--log.level=INFO'

            # Global redirect
            - '--entrypoints.web.http.redirections.entrypoint.to=websecure'
            - '--entrypoints.web.http.redirections.entrypoint.scheme=https'

            # --- Add these lines for Let's Encrypt ---
            - '--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json'
            - '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web'

            # Disable timeout
            - '--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0'
            - '--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0'

        labels:
            # Watchtower
            - 'com.centurylinklabs.watchtower.enable=true'

        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - letsencrypt:/letsencrypt

    postgres:
        image: postgres:18
        container_name: postgres-db
        restart: unless-stopped
        environment:
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: supersecretpassword
            POSTGRES_DB: chithi
        volumes:
            - postgres_data:/var/lib/postgresql
        healthcheck:
            test: ['CMD-SHELL', 'pg_isready']
            interval: 10s
            timeout: 5s
            retries: 5
    volume-permission-helper:
        image: alpine
        volumes:
            - rustfs:/data
        command: >
            sh -c "
              chown -R 10001:10001 /data &&
              echo 'Volume Permissions fixed' &&
              exit 0
            "
        restart: 'no'

    rustfs:
        image: rustfs/rustfs:latest
        security_opt:
            - 'no-new-privileges:true'
        container_name: rustfs
        restart: unless-stopped
        user: '10001:10001'
        volumes:
            - rustfs:/data
        labels:
            # Watchtower
            - 'com.centurylinklabs.watchtower.enable=true'

        environment:
            RUSTFS_ADDRESS: '0.0.0.0:9000'
            RUSTFS_CONSOLE_ADDRESS: '0.0.0.0:9001'
            RUSTFS_VOLUMES: '/data/rustfs'
            RUSTFS_ACCESS_KEY: 'rustfsadmin'
            RUSTFS_SECRET_KEY: 'rustfsadmin'
            RUSTFS_CONSOLE_ENABLE: 'true'
        healthcheck:
            test:
                [
                    'CMD',
                    'sh',
                    '-c',
                    'curl -f http://localhost:9000/health && curl -f http://localhost:9001/rustfs/console/health',
                ]
            interval: 30s
            timeout: 10s
            retries: 3
            start_period: 40s

        depends_on:
            volume-permission-helper:
                condition: service_completed_successfully
    redis:
        image: redis:8.6-alpine
        container_name: redis
        restart: unless-stopped
        volumes:
            - redis_data:/data
        healthcheck:
            test: ['CMD', 'redis-cli', 'ping']
            interval: 30s
            timeout: 10s
            retries: 3

    backend:
        image: ghcr.io/chithi-dev/chithi-backend:latest
        container_name: backend
        restart: unless-stopped
        command: /bin/sh /app/scripts/start_backend.sh
        labels:
            - 'traefik.enable=true'
            - 'traefik.http.routers.backend.rule=Host(`<your_domain>`) && PathPrefix(`/api`)'

            # This middleware strips /api from the URL before it hits the container
            - 'traefik.http.middlewares.api-strip.stripprefix.prefixes=/api'
            - 'traefik.http.routers.backend.middlewares=api-strip'
            - 'traefik.http.routers.backend.entrypoints=websecure'
            - 'traefik.http.services.backend.loadbalancer.server.port=8000'

            # Enable letsencrypt
            - 'traefik.http.routers.backend.tls=true'
            - 'traefik.http.routers.backend.tls.certresolver=letsencrypt'

            # Watchtower
            - 'com.centurylinklabs.watchtower.enable=true'

        environment: &backend-variable
            POSTGRES_SERVER: postgres
            POSTGRES_PORT: 5432
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: supersecretpassword
            POSTGRES_DB: chithi
            RUSTFS_ENDPOINT_URL: http://rustfs:9000
            RUSTFS_ACCESS_KEY: rustfsadmin
            RUSTFS_SECRET_ACCESS_KEY: rustfsadmin
            CELERY_BROKER_URL: redis://redis:6379/0
            CELERY_RESULT_BACKEND: redis://redis:6379/0

            # Redis
            REDIS_ENDPOINT: redis://redis:6379/1

            # Running behind reverse proxy
            ROOT_PATH: /api

        depends_on:
            postgres:
                condition: service_healthy
            redis:
                condition: service_healthy
            rustfs:
                condition: service_healthy
        networks:
            - default

    celery:
        image: ghcr.io/chithi-dev/chithi-backend:latest
        container_name: celery
        restart: unless-stopped
        command: /bin/sh /app/scripts/start_celery.sh
        environment: *backend-variable
        depends_on:
            postgres:
                condition: service_healthy
            redis:
                condition: service_healthy
            backend:
                condition: service_started

    frontend:
        image: ghcr.io/chithi-dev/chithi-frontend-node:latest
        container_name: frontend
        restart: unless-stopped
        labels:
            # Traefik
            - 'traefik.enable=true'
            - 'traefik.http.routers.frontend.rule=Host(`<your_domain>`)'
            - 'traefik.http.routers.frontend.priority=1'
            - 'traefik.http.routers.frontend.entrypoints=websecure'
            - 'traefik.http.routers.frontend.tls=true'
            - 'traefik.http.routers.frontend.tls.certresolver=letsencrypt'
            - 'traefik.http.services.frontend.loadbalancer.server.port=3000'
            - "traefik.http.middlewares.coep.headers.customResponseHeaders.Cross-Origin-Opener-Policy=same-origin"
            - "traefik.http.middlewares.coep.headers.customResponseHeaders.Cross-Origin-Embedder-Policy=require-corp"
            - "traefik.http.middlewares.coep.headers.customResponseHeaders.Cross-Origin-Resource-Policy=same-origin"
            - "traefik.http.routers.frontend.middlewares=coep@docker"
            # Watchtower
            - 'com.centurylinklabs.watchtower.enable=true'
        environment:
            PUBLIC_BACKEND_API: https://<your_domain>/api
        depends_on:
            backend:
                condition: service_started

    watchtower:
        image: nickfedor/watchtower
        container_name: watchtower
        restart: unless-stopped
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
        environment:
            - WATCHTOWER_CLEANUP=true
            - WATCHTOWER_POLL_INTERVAL=60
            # WATCHTOWER_SCHEDULE: "0 0 4 * * *"   # optional: daily at 4am

volumes:
    postgres_data:
    redis_data:
    rustfs:
    letsencrypt:

Danger

Please replace <your_domain> with the actual domain you are going to use to point to chithi instance.

If you still have any issues around hosting your instances, please open a discussion