Files
linearvoid/docs/COOLIFY.md
T
zwitschi 11d48c8e0d
CI / test (push) Successful in 3m3s
CI / docker (push) Successful in 24s
chore(env): update PLAYOUT_PORT to 8081 and normalize line endings
2026-09-02 20:59:57 +02:00

8.3 KiB

Coolify Deployment

Last Updated: 2026-08-28

Alternative deployment path for Linear Void using Coolify (v4.3.14), an open-source self-hostable PaaS that manages servers, apps, and databases over SSH. Drop-in alternative to the flows in SETUP.md: local dev (docker-compose.yml) and test-lab/prod (docker-compose.prod.yml via Gitea Actions CD). Coolify deploys the same Compose stack through a web UI and its own Traefik reverse proxy for automatic HTTPS, domain routing, and one-click redeploys (no manual DOCKER_HOST).

Why Coolify

  • Single control plane: manage playout, chat, nginx, redis, db (plus exporters/monitoring) from one dashboard.
  • Automatic TLS: Let's Encrypt provisioning/renewal per app/domain, replacing the manual Cloudflare + nginx origin TLS setup for the app hostname.
  • No vendor lock-in: config lives on your server; containers keep running if you stop using Coolify — you only lose automation.
  • Simpler cutover: on Hetzner VPS provisioning, point Coolify at the new server and redeploy instead of editing CD DOCKER_HOST.

Replaces the deployment orchestration layer only. Cloudflare DNS, R2 media bucket, cache rules, and the edge Worker from SETUP.md are unchanged.

Prerequisites

  • Linux server with Docker Engine (any supported distro).
  • SSH access (root or passwordless sudo) from the Coolify install machine — Coolify provisions the target over SSH.
  • 2 GB RAM / 20 GB disk minimum for Coolify itself (plus resources for the stack).
  • A domain pointed at the server for HTTPS (e.g. linearvoid.tv).
  • Ports 80 and 443 reachable for Traefik.
  • Gitea registry credentials (CI_REGISTRY_USER / CI_REGISTRY_TOKEN) if pulling prebuilt playout, chat, nginx images.

Installation

Recommended on a dedicated server:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

The installer boots a Docker-based Coolify instance and prints the URL and default credentials. Complete first-run setup in the dashboard. For non-interactive/offline installs, see the installation docs and install script source.

Add the Linear Void server

  1. Servers → Add.
  2. Choose your provider (or Localhost if Coolify runs on the same host as the stack).
  3. Provide SSH details; for a remote host Coolify validates and installs its agent.
  4. Confirm the server shows Reachable before creating resources.

Current topology (verified 2026-08-30)

Active test-lab setup is two-tier:

Component Host Exposes Docker TCP Notes
Coolify dashboard 192.168.88.18 https://coolify.allucanget.biz via Cloudflare tunnel (coolify-cloudflared); dashboard at 8000 No Manages target servers over SSH
Deployment target 192.168.88.40 coolify-proxy = Traefik on 80/443; port 8000 bound by Portainer, not Coolify Yes Per-server helpers (coolify-proxy, coolify-sentinel, coolify-cloudflared) + the stack via docker-compose.prod.yml (linearvoid compose project, 11 containers)

The main coolify app container runs only on the dashboard host 192.168.88.18; Compose-resource services land on the target 192.168.88.40. The dashboard host exposes no Docker TCP socket, so run the checks below against the target 192.168.88.40:

./scripts/check-env.sh      # env/deployment health on target (containers, helpers, health)
./scripts/check-tokens.sh   # Gitea registry, Cloudflare, R2, admin credential access

Deploy the stack

Deploy as a Docker Compose resource (not individual apps):

  1. Projects → Add → Docker Compose.
  2. Select the target server.
  3. In Build Settings, set the compose file source:
    • Git Repository → point at https://git.allucanget.biz/zwitschi/linearvoid.git, compose file docker-compose.prod.yml (Coolify pulls on each redeploy).
    • Raw Compose → paste docker-compose.prod.yml directly to manage manually.
  4. For the Git source, add the Gitea registry login as a Deployment Key / environment secret so playout, chat, nginx images can be pulled. Alternatively switch those services to build: blocks (as in docker-compose.yml) to build from source on the server.

Environment variables

Map Linear Void's .env values into Coolify's Environment Variables tab:

Variable Purpose
DATABASE_URL postgres://linearvoid:<password>@db:5432/linearvoid?sslmode=disable
REDIS_URL redis://redis:6379/0
POSTGRES_PASSWORD DB password (used by db service)
ADMIN_USERNAME / ADMIN_PASSWORD Admin panel credentials
ADMIN_SESSION_TTL_HOURS Admin session lifetime
R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY Cloudflare R2 credentials
R2_BUCKET_NAME / R2_ENDPOINT / R2_REGION R2 bucket config (R2_REGION=auto)
MEDIA_DOMAIN media.linearvoid.tv
VITE_PUBLIC_APP_URL https://linearvoid.tv
VITE_PUBLIC_WS_URL wss://linearvoid.tv/ws
VITE_PUBLIC_HLS_URL https://media.linearvoid.tv/hls/playlist.m3u8

Keep internal service URLs on compose hostnames (redis, db) exactly as in docker-compose.prod.yml.

Persistent storage

Named volumes (redis-data, postgres-data) and bind mounts under ./data must be configured under Persistent Storage to survive redeploys:

Mount Purpose
data/playlist Playout playlist file
data/media Normalized source videos (read-only to playout)
data/hls Generated HLS manifests/segments
data/chat Chat service working data
data/slate Slate media
redis-data Redis append-only persistence
postgres-data PostgreSQL data directory

If Coolify doesn't auto-create the dirs, create them before first deploy:

mkdir -p data/playlist data/media data/hls data/chat

Domains and proxy

  1. Assign the nginx service a public domain (e.g. linearvoid.tv); Traefik terminates TLS and forwards to nginx.
  2. Set Ports Exposes so Traefik targets the nginx port (80 inside container, mapped from ${NGINX_PORT:-8082}:80).
  3. Do not publicly expose playout (8081) or chat (3000) health ports — keep them internal and route through nginx.
  4. For media.linearvoid.tv, keep serving HLS from the R2 custom domain (per SETUP.md) rather than routing through Coolify, so segments stay on Cloudflare's edge cache.

Post-deploy verification

  • docker compose ps on the server shows all services healthy.
  • Playout health: https://<host>/health via nginx (internal :8081/health).
  • Chat health: internal :3000/health.
  • HLS written to data/hls and served from media.linearvoid.tv.
  • Chat WebSocket connects over wss://linearvoid.tv/ws.
  • Admin login works at /admin with configured credentials.

Monitoring

Coolify exposes container logs/basic metrics per resource. The stack ships Prometheus + Grafana (monitoring/) plus nginx/redis exporters. To reach them via Coolify, expose Grafana on a restricted subdomain (e.g. grafana.linearvoid.tv) and keep Prometheus internal, mirroring SETUP.md access guidance.

Cutover from Gitea CD

  1. Deploy the stack as a Coolify compose resource (above).
  2. Point Cloudflare DNS for linearvoid.tv / www at the server IP (proxied).
  3. Disable (or keep as fallback) the cd job in the Gitea workflow so the two orchestrators don't fight over the same DOCKER_HOST.
  4. Keep the Gitea CI (build + push images) job so Coolify keeps pulling :latest from the registry.

Troubleshooting

  • Image pull fails: set Gitea registry credentials in the compose secret store, or switch services to build: blocks.
  • Healthchecks fail: confirm curl exists in the built images (healthchecks rely on it).
  • TLS errors: check the domain's DNS points at the Coolify server and ports 80/443 are open.
  • HLS not updating: verify data/hls is writable and playout restarts cleanly; see SETUP.md normalization troubleshooting.
  • Ports conflict: Coolify's Traefik binds 80/443; do not also map those to the host in the compose file.

Further reading