Deploying sentrinel.dev
Three static sites on Cloudflare, one application stack on your own servers. Nothing here needs a secret in the repository.
The shape of it
| Host | Serves | Where it runs |
|---|---|---|
sentrinel.dev |
marketing site | Cloudflare Pages |
www.sentrinel.dev |
redirect to the apex | Cloudflare rule |
docs.sentrinel.dev |
documentation | Cloudflare Pages |
app.sentrinel.dev |
the dashboard | Cloudflare Pages |
api.sentrinel.dev |
the Sentrinel API | application server, via Dokploy |
status.sentrinel.dev |
public status pages | the same API |
The dashboard is on the CDN rather than the server because every one of its 24 pages prerenders โ it is a client-side app that calls the API cross-origin, with no route handlers and no middleware. That means no Node process to keep alive, no cold starts, and the origin only ever serves API traffic.
The API cannot go on Cloudflare: it holds long-lived connections to Postgres and ClickHouse, runs the alert checker and uptime prober on timers, and streams SSE. It belongs on the box with the databases.
Every record is proxied (orange cloud), so the origin IP is never published and TLS terminates at the edge.
1. Credentials
cp .env.deploy.example .env.deploy
Fill it in. The file is gitignored and is the only place these values live.
Use a scoped API Token, not the Global API Key. A Global key authenticates as your entire account with no way to narrow it โ anything holding it can change billing, delete zones and read every other domain you own, and it cannot be revoked without breaking everything else using it. A token can be limited to exactly this:
- Account ยท Cloudflare Pages ยท Edit
- Zone ยท DNS ยท Edit
- Zone Resources โ Include โ Specific zone โ
sentrinel.dev
Create one at dash.cloudflare.com/profile/api-tokens.
2. All of it, one command
bun run deploy
Preflight โ DNS โ build and publish the three sites โ preflight again. It stops at the first real problem rather than half-deploying, and every step below can also be run on its own.
To look without touching anything:
bun run deploy:check
That reads the token and its scopes, every DNS record, whether the Pages projects exist and have their custom domains, whether all five hostnames answer, and whether Dokploy is reachable โ and says what it found for each. It is the right thing to paste when you want someone to diagnose a deploy.
3. DNS
bun run deploy:dns # prints the plan, changes nothing
bun run deploy:dns -- --apply
It reconciles rather than appends, so running it twice is a no-op, and it lists but never touches records it does not manage โ your mail and verification records are safe.
4. The three sites
bun run deploy:pages # all three
bun run deploy:pages dashboard # or one at a time
First publish only: attach the custom domain to each project under Workers & Pages โ <project> โ Custom domains.
NEXT_PUBLIC_API_URL is compiled into the dashboard bundle, so pointing it at a
different API is a rebuild, not a restart.
www โ apex
Cloudflare dashboard โ Rules โ Redirect Rules โ Create:
- When incoming requests match:
Hostname equals www.sentrinel.dev - Then: Dynamic redirect, 301, expression
concat("https://sentrinel.dev", http.request.uri.path)
5. The API on Dokploy
Your Dokploy already runs other projects; this adds one more beside them.
Create the project
Dokploy โ Projects โ Create โ sentrinel โ Add Service โ Compose.
Point it at this repository, compose file docker-compose.prod.yml.
Environment
Set these in Dokploy's environment editor โ not in a committed file:
POSTGRES_PASSWORD=<generate>
CLICKHOUSE_PASSWORD=<generate>
SENTRINEL_REQUIRE_AUTH=true
SENTRINEL_REQUIRE_INGEST_KEY=true
SENTRINEL_ALLOWED_ORIGINS=https://app.sentrinel.dev
PUBLIC_API_URL=https://api.sentrinel.dev
PUBLIC_DASHBOARD_URL=https://app.sentrinel.dev
TELEMETRY_STORE=clickhouse
SENTRINEL_ALLOWED_ORIGINS is the security boundary, not a convenience: the
dashboard sends its session cookie with every request, so an origin that is not
on this list must not be able to call the API. Unset means localhost only.
Domain
Dokploy โ the api service โ Domains โ Add:
- Host
api.sentrinel.dev, port3001, HTTPS on - Certificate: none โ Cloudflare already terminates TLS at the edge
Add status.sentrinel.dev the same way if you want the public status pages on
their own host.
Database server
If Postgres and ClickHouse live on the second box rather than in the compose file, remove those two services and point the API at them:
DATABASE_URL=postgres://sentrinel:<password>@75.119.137.38:5432/sentinel
CLICKHOUSE_URL=http://75.119.137.38:8123
Then restrict both to the application server's address โ a database reachable from the whole internet is the single most common way these deployments are lost:
ufw allow from 194.163.176.236 to any port 5432 proto tcp
ufw allow from 194.163.176.236 to any port 8123 proto tcp
ufw deny 5432
ufw deny 8123
First run
docker compose -f docker-compose.prod.yml exec api \
bun run --cwd packages/db ch:migrate
Then open https://app.sentrinel.dev and create the first account โ it becomes
the owner of a new organization.
6. Check it
curl -s https://api.sentrinel.dev/health
{ "status": "ok", "telemetryStore": "clickhouse", "telemetryHealthy": true }
The API prints its security posture at boot. Read it after every deploy:
๐ก๏ธ Sentrinel API on :3001
auth required
ingest key required
telemetry clickhouse
CORS https://app.sentrinel.dev
auth OFF or a CORS line reading localhost only means the environment did not
reach the container.
Rotating a credential
Any secret that has been pasted into a chat, a ticket, or a screenshot is spent โ treat it as public and replace it:
Cloudflare โ delete the token at profile/api-tokens, create a replacement, update
.env.deploy. Roll the Global API Key too, from the same page, if one was ever shared.Dokploy โ Settings โ API Keys โ revoke and reissue.
Servers โ
passwdon each host, then move to keys and turn password auth off entirely:ssh-copy-id zaga@194.163.176.236 # then, in /etc/ssh/sshd_config: # PasswordAuthentication no sudo systemctl reload sshA password on a public-facing host is brute-forced continuously; a key is not.
Database โ
ALTER USER sentrinel WITH PASSWORD 'โฆ', then update Dokploy's environment and redeploy.