statichost dynamically creates subdomains, which makes it tricky to deploy with reverse proxies that expect a static list of hostnames (Coolify, Caprover, most one-click PaaS). Pick the recipe that matches your setup:
| Recipe | When to use | TLS | DNS |
|---|---|---|---|
docker-compose.caddy.yml |
Self-hosted on a VPS with public ports 80/443 | Caddy on-demand (Let's Encrypt) | Wildcard A record at your registrar |
docker-compose.cloudflared.yml |
Anywhere — VPS, Coolify, behind NAT | Cloudflare edge | Cloudflare Zero Trust tunnel auto-creates DNS |
| Coolify with a subdomain list | You already use Coolify and want it to manage TLS | Coolify-managed (Let's Encrypt per subdomain) | Each subdomain registered in Coolify's Domain field |
| Coolify with cloudflared bypass | Coolify + you need unbounded wildcards | Cloudflare edge | Wildcard via cloudflared |
The simplest fully-self-hosted option. Caddy ships in front of statichost, requests Let's Encrypt certs the first time each subdomain is hit, and proxies everything to statichost. No DNS API access, no Cloudflare account.
Requirements:
- A VPS with public IPv4 + ports 80 and 443 free
- Wildcard DNS
*.your-domain.compointing to that VPS - (Optional) apex
your-domain.compointing to the same IP
cd deploy
STATICHOST_TOKEN=$(openssl rand -hex 32) \
STATICHOST_DOMAIN=your-domain.com \
docker compose -f docker-compose.caddy.yml up -dLimitations:
- Let's Encrypt rate-limits 50 certs/week per registered domain. Plenty for static deploys; tight if you spawn many ephemeral tunnel names.
- Any hostname that resolves to your IP triggers a cert request. Restrict your DNS wildcard scope if abuse is a concern.
statichost runs in its own container, with a cloudflared sidecar that opens an outbound tunnel to Cloudflare. Cloudflare terminates TLS on its edge and forwards all *.your-domain.com traffic through the tunnel. Works on any host with outbound internet — no public IP, no port forwarding.
Setup:
- In Cloudflare Zero Trust → Networks → Tunnels → Create a tunnel → Cloudflared → name it
statichost. - Skip the "Install cloudflared" step (the docker container does this); copy the token shown after creation.
- Click Next → Add a public hostname:
- Subdomain:
* - Domain: your-domain.com
- Service: HTTP —
http://statichost:3000
- Subdomain:
- Save.
cd deploy
STATICHOST_TOKEN=$(openssl rand -hex 32) \
STATICHOST_DOMAIN=your-domain.com \
CLOUDFLARED_TOKEN=eyJhIj... \
docker compose -f docker-compose.cloudflared.yml up -dCloudflare automatically creates the wildcard DNS for you.
The pragmatic Coolify path. Coolify's reverse proxy can't do wildcard routing — its Domain field generates one literal Host(...) Traefik rule per item. Wildcards like *.example.com either get treated as a literal * or do nothing. So you maintain a comma-separated list of every subdomain you want, and Coolify routes each one to statichost.
Counter-intuitive but it works: every subdomain in the list points to the same statichost container, which then routes by Host header internally. New subdomain? Edit the field, click save, redeploy — Coolify applies new Traefik rules in seconds.
- New resource → Docker Image, image:
jacobmoura7/statichost:latest - Domains field: paste your subdomain list as
http://...URLs separated by commas. A starter set covering common cases:Thehttp://statichost.your-domain.com,http://www.your-domain.com,http://app.your-domain.com,http://blog.your-domain.com,http://docs.your-domain.com,http://demo.your-domain.com,http://preview.your-domain.com,http://staging.your-domain.comstatichost.your-domain.comentry is the API host (CLI talks to it). The rest are slots for future deploys/tunnels. - Environment variables:
STATICHOST_TOKEN—openssl rand -hex 32(mark as secret)STATICHOST_DOMAIN— your domain, no scheme, no*.
- Persistent storage:
/data/statichost/sites→/sites - Network port exposed:
3000 - Save and deploy.
Coolify will issue a Let's Encrypt cert for each subdomain (HTTP-01 challenge per hostname).
Edit the Domains field, append http://newname.your-domain.com, click save. Coolify regenerates Traefik rules and provisions the new cert. Then statichost deploy ./folder --name newname works.
When you save a comma-separated list, Coolify shows a yellow warning saying "not all services support multiple subdomains". This is a generic warning aimed at apps that embed their own canonical URL in HTML/emails. statichost handles multiple subdomains by design — every request is routed by its Host header, no global "self URL" concept. Ignore the warning.
If you want to silence it permanently, the only path today is contributing a accepts_wildcards flag to the Coolify project so service templates can opt out — see coollabsio/coolify on GitHub.
- Each subdomain has to exist in the Coolify list before you deploy to it. No truly dynamic creation. If you need ephemeral / random names (e.g., per-PR previews with auto-generated names), use the cloudflared bypass below instead.
- Coolify charges no Let's Encrypt rate budget, but Let's Encrypt itself caps at 50 issuances/week per registered domain. Long enough lists or frequent recreations can hit that.
You already have Coolify running other apps and want unlimited dynamic subdomains for statichost (no list maintenance). Deploy statichost as a Coolify resource but route around Coolify's Traefik using cloudflared.
- In Coolify, deploy
docker-compose.cloudflared.ymlas a new resource (Service → Docker Compose Stack). - Leave the Domain field blank — critical. With Domain empty, Coolify doesn't generate Traefik labels and the cloudflared sidecar handles routing instead.
- Set environment variables
STATICHOST_TOKEN,STATICHOST_DOMAIN,CLOUDFLARED_TOKENin the resource's UI. - Deploy.
Coolify runs both containers without touching their routing. cloudflared opens an outbound tunnel to Cloudflare and forwards *.your-domain.com traffic directly to the statichost container.
Other apps on Coolify continue working — they live behind Coolify's Traefik on a different routing path.
| Subdomain list | cloudflared bypass | |
|---|---|---|
| Subdomain creation | Edit Coolify field + redeploy | Just statichost deploy --name X |
| TLS | Coolify Let's Encrypt per name | Cloudflare edge |
| Unbounded names | ❌ | ✅ |
| Cloudflare account required | No | Yes |
| Coolify warning on save | Yes (harmless) | None |
| Best for | Stable list of predictable subdomains | Per-PR previews, random tunnel names |