12 KiB
Coolify Deployment
Last Updated: 2026-08-28
This guide describes an alternative deployment path for Linear Void using Coolify (v4.3.14) — an open-source, self-hostable PaaS that lets you manage servers, applications, and databases over SSH without vendor lock-in.
Coolify is a drop-in alternative to the two existing deployment flows described in SETUP.md:
- Local development (
docker-compose.yml) — builds from source on your workstation. - Test-lab / production (
docker-compose.prod.yml) — pulls prebuilt images from the Gitea registry and is driven by the Gitea Actions CD workflow.
With Coolify, the same Docker Compose stack is deployed through a web UI and
Coolify's own reverse proxy (Traefik), giving you automatic HTTPS, domain
routing, and one-click redeploys without managing DOCKER_HOST manually.
Why use Coolify?
- Single control plane: manage the playout, chat, nginx, redis, and db services (plus exporters and monitoring) from one dashboard.
- Automatic TLS: Coolify provisions and renews Let's Encrypt certificates per application/domain, replacing the manual Cloudflare + nginx origin TLS dance for the app hostname.
- No vendor lock-in: all configuration lives on your server. If you stop using Coolify, the running containers keep working; you only lose the automation.
- Simpler cutover: when the Hetzner VPS is provisioned, point Coolify at the
new server and redeploy instead of editing CD
DOCKER_HOSTvalues.
Note
: Coolify replaces the deployment orchestration layer only. Cloudflare DNS, the R2 media bucket, cache rules, and the edge Worker described in SETUP.md still apply and are unchanged.
Prerequisites
- A Linux server running Docker Engine (any distro Coolify supports).
- SSH access (root or a user with passwordless
sudo) from the machine where you run the Coolify install — Coolify uses SSH to provision the target server. - At least 2 GB RAM / 20 GB disk for the Coolify instance itself (plus additional resources for the Linear Void stack).
- A domain name pointed at the server for HTTPS (e.g.
linearvoid.tv). - Ports
80and443reachable on the server for Traefik. - The Gitea registry credentials (
CI_REGISTRY_USER/CI_REGISTRY_TOKEN) if you intend to pull the prebuiltplayout,chat, andnginximages.
Installation
Install Coolify on a dedicated server (or alongside your stack — but a dedicated Coolify server is the recommended layout):
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. Open the dashboard in your browser and complete first-run setup.
For non-interactive or offline installs, see the installation docs and the install script source.
Add the Linear Void server
- In the Coolify dashboard, go to Servers → Add.
- Choose your provider (or Localhost if Coolify runs on the same machine as the stack).
- Provide the SSH details. For a remote host, Coolify validates the connection and installs its agent.
- Verify the server shows as Reachable before creating resources.
Current topology (verified 2026-08-30)
The active test-lab setup is two-tier:
| Component | Host | Environment | Exposes | Docker TCP | Notes |
|---|---|---|---|---|---|
| Coolify dashboard | 192.168.88.18 |
Coolify; docker container; coolify dashboard at 8000 |
https://coolify.allucanget.biz via Cloudflare tunnel (coolify-cloudflared) |
No | Coolify manages target servers over SSH |
| Deployment target | 192.168.88.40 |
Only Linear Void deploys here | coolify-proxy = Traefik on 80/443, Port 8000 on this host is bound by Portainer, not Coolify. |
Yes | Contains Coolify's per-server helpers (coolify-proxy, coolify-sentinel, coolify-cloudflared) plus the stack via docker-compose.prod.yml (the linearvoid compose project, 11 containers). |
The main coolify app container does not run on 192.168.88.40; it lives on
the dashboard host 192.168.88.18. When a Coolify deployment creates a Compose
resource, the app services land on the target server 192.168.88.40.
Useful checks:
# Environment / deployment health on the target 192.168.88.40 (containers, helpers, health)
./scripts/check-env.ps1
# Credential / token access (Gitea registry, Cloudflare, R2, admin login)
./scripts/check-tokens.ps1
The Coolify dashboard runs on the 192.168.88.18 container host (port 8000,
publicly https://coolify.allucanget.biz) and exposes no Docker TCP socket, so
the environment/token scripts are intended to run against the deployment target
192.168.88.40, not 192.168.88.18.
Deploy the stack
Linear Void is a multi-service Docker Compose stack, so deploy it as a Compose (Docker Compose) resource rather than individual applications.
- Projects → Add → Docker Compose.
- Select the target server.
- In Build Settings, set the compose file source:
- Git Repository — point Coolify at
https://git.allucanget.biz/zwitschi/linearvoid.gitand usedocker-compose.prod.ymlas the compose file. Coolify pulls on each redeploy. - Raw Compose — paste the contents of
docker-compose.prod.ymldirectly if you prefer to manage it manually.
- Git Repository — point Coolify at
- If using the Git source, add the Gitea registry login as a Deployment Key /
environment secret so the
playout,chat, andnginximages can be pulled. Alternatively, switch those services tobuild:blocks (as indocker-compose.yml) so Coolify builds from source on the server.
Environment variables
Coolify injects a secret store into the compose resource. Map the Linear Void
environment to Coolify's Environment Variables tab (these correspond to the
values normally placed in .env):
| Variable | Purpose |
|---|---|
DATABASE_URL |
postgres://linearvoid:<password>@db:5432/linearvoid?sslmode=disable |
REDIS_URL |
redis://redis:6379/0 |
POSTGRES_PASSWORD |
Database password (used by the 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
The compose file uses named volumes (redis-data, postgres-data) and bind
mounts under ./data (playlist, media, hls, chat, slate). Configure these under
Persistent Storage so data survives redeploys:
| Mount | Purpose |
|---|---|
data/playlist |
Playout playlist file |
data/media |
Normalized source videos (read-only to playout) |
data/hls |
Generated HLS manifests and segments |
data/chat |
Chat service working data |
data/slate |
Slate media |
redis-data |
Redis append-only persistence |
postgres-data |
PostgreSQL data directory |
Create the data subdirectories before the first deploy if Coolify does not
auto-create them (mkdir -p data/playlist data/media data/hls data/chat).
Domains and proxy
- Open the compose resource and assign the nginx service a public domain
(e.g.
linearvoid.tv). Coolify's Traefik reverse proxy then terminates TLS and forwards to thenginxcontainer. - Set the Ports Exposes mapping so Traefik targets the nginx service port
(
80inside the container, mapped from${NGINX_PORT:-8080}:80). - Do not also expose the playout (
8081) or chat (3000) health ports publicly — keep those internal to the compose network and let Traefik route through nginx. - For the media domain (
media.linearvoid.tv), keep serving HLS from the R2 custom domain (per SETUP.md) rather than routing it through Coolify, so segments stay on Cloudflare's edge cache.
Post-deploy verification
After the first deploy, confirm:
docker compose ps(on the server) shows all services healthy.- Playout health:
https://<host>/healthvia nginx (internal:8081/health). - Chat health: internal
:3000/health. - HLS is written to
data/hlsand served frommedia.linearvoid.tv. - Chat WebSocket connects over
wss://linearvoid.tv/ws. - Admin login works at
/adminwith the configured credentials.
Monitoring
Coolify exposes container logs and basic metrics per resource. The stack also
ships Prometheus + Grafana (monitoring/) and nginx/redis exporters. If you
want those reachable through Coolify, expose Grafana on a restricted subdomain
(e.g. grafana.linearvoid.tv) and keep Prometheus internal, mirroring the
access guidance in SETUP.md.
Cutover from Gitea CD
Coolify can fully replace the Gitea Actions CD step. To switch:
- Deploy the stack as a Coolify compose resource (above).
- Point Cloudflare DNS for
linearvoid.tv/wwwat the server IP (proxied). - Disable (or keep as a fallback) the
cdjob in the Gitea workflow so the two orchestrators don't fight over the sameDOCKER_HOST. - Keep the Gitea CI (build + push images) job so Coolify continues pulling
:latestimages from the registry.
Troubleshooting
- Image pull fails: ensure the Gitea registry credentials are set in the
compose resource's secret store, or switch the services to
build:blocks. - Healthchecks fail: confirm
curlis available in the built images (the compose healthchecks rely oncurl). - TLS errors: check the domain's DNS points at the Coolify server and ports
80/443are open. - HLS not updating: verify
data/hlsis writable and the playout service restarts cleanly; see the normalization troubleshooting in SETUP.md. - Ports conflict: Coolify's Traefik binds
80/443; do not also map those to the host in the compose file.
Further reading
- Coolify repo (v4.3.14): https://github.com/coollabsio/coolify/tree/v4.3.14
- Coolify docs: https://coolify.io/docs
- Linear Void deployment: DEPLOYMENT.md
- Linear Void setup: SETUP.md