# J621 — default deployment: frontend + backend + database on one Tailscale # host, behind the nginx proxy service (no host nginx, nothing published on # the host's ports). # # cd deploy && cp .env.example .env # fill in TS_AUTHKEY, SECRET_KEY, ... # docker compose up -d # or: docker compose -f compose.yml up -d # # Tailnet-only: no Funnel, so the service is reachable from your tailnet # (https://..ts.net) but not from the public internet. # The nginx service routes /api, /admin, /static and /health to the # backend and everything else to the SPA. Same origin, so no CORS needed. name: j621-deploy-tailnet services: mariadb: image: mariadb:11.4 restart: unless-stopped environment: MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-j621root} MARIADB_DATABASE: ${DB_NAME:-j621} MARIADB_USER: ${DB_USER:-j621} MARIADB_PASSWORD: ${DB_PASSWORD:-j621} volumes: - ./data/mariadb:/var/lib/mysql healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 5s timeout: 5s retries: 20 redis: image: redis:7-alpine restart: unless-stopped command: ["redis-server", "--appendonly", "yes"] volumes: - ./data/redis:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 20 backend: image: ${J621_REGISTRY:-gitea.rainbow-herring.ts.net/jakebreath}/j621-backend:${J621_TAG:-latest} build: context: .. dockerfile: deploy/J621-Backend # Bake the commit into the image so the shell's version pill shows it; # the push scripts pass --build-arg themselves. args: GIT_HASH: ${GIT_HASH:-unknown} restart: unless-stopped env_file: [./.env] environment: DB_HOST: mariadb DB_PORT: "3306" REDIS_URL: redis://redis:6379/1 # The tailnet funnel terminates TLS and forwards the original host/proto. TRUST_PROXY_HEADERS: "true" volumes: - ./data/media:/app/media - ./data/logs:/app/logs depends_on: mariadb: condition: service_healthy redis: condition: service_healthy # Periodic maintenance inside the deployment (no host cron): follow syncs, # similarity cleanup and the guest blacklist refresh. scheduler: image: ${J621_REGISTRY:-gitea.rainbow-herring.ts.net/jakebreath}/j621-backend:${J621_TAG:-latest} build: context: .. dockerfile: deploy/J621-Backend args: GIT_HASH: ${GIT_HASH:-unknown} restart: unless-stopped entrypoint: ["j621-scheduler"] env_file: [./.env] environment: DB_HOST: mariadb DB_PORT: "3306" REDIS_URL: redis://redis:6379/1 SYNC_EVERY: ${J621_SYNC_EVERY:-1800} CLEAN_EVERY: ${J621_CLEAN_EVERY:-3600} BLACKLIST_EVERY: ${J621_BLACKLIST_EVERY:-86400} volumes: - ./data/media:/app/media depends_on: mariadb: condition: service_healthy redis: condition: service_healthy frontend: image: ${J621_REGISTRY:-gitea.rainbow-herring.ts.net/jakebreath}/j621-frontend:${J621_TAG:-latest} build: context: .. dockerfile: deploy/J621-Frontend restart: unless-stopped volumes: # Desktop update feed (deploy/push_desktop.sh): latest*.yml + installers. - ./data/desktop:/usr/share/nginx/html/desktop:ro nginx: image: nginx:1.29-alpine restart: unless-stopped volumes: - ./nginx-proxy.conf:/etc/nginx/conf.d/default.conf:ro depends_on: backend: condition: service_healthy frontend: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1/nginx-health || exit 1"] interval: 30s timeout: 3s retries: 3 start_period: 10s tailscale: image: tailscale/tailscale:latest restart: unless-stopped # Shares the nginx service's network namespace: 127.0.0.1:80 is the proxy. network_mode: "service:nginx" environment: TS_AUTHKEY: ${TS_AUTHKEY:?Set TS_AUTHKEY in deploy/.env} TS_HOSTNAME: ${TS_HOSTNAME:-j621} TS_AUTH_ONCE: "true" TS_STATE_DIR: /var/lib/tailscale TS_SERVE_CONFIG: /config/serve.json volumes: - ./tailscale-state:/var/lib/tailscale - ./serve.default.tailnet.json:/config/serve.json:ro - /etc/ssl/certs:/etc/ssl/certs:ro depends_on: nginx: condition: service_healthy healthcheck: test: ["CMD", "tailscale", "status"] interval: 30s timeout: 5s retries: 3 start_period: 30s