Skip to content

Repository files navigation

Noite

Tiny self-hostable PaaS for celld. Spec: SPEC.md.

cp .env.example .env
make up      # release runner + Oxide UI
make dev     # cargo-watch runner + Oxide Vite HMR
make logs
Path
apps/runner Rust runner (deploy, fleets, caddy)
apps/noite Oxide control UI (passkeys, workflow/queue/schedule → runner)
apps/noite/test sample app + deploy.sh
docker/compose.yaml stack
docker/compose.coolify.yaml production stack for Coolify (automatic generated domain, generated secrets, healthchecks)
docker/compose.byob.yaml external-S3 overlay (make up-byob), bundled RustFS excluded
URL
http://localhost:9080 control UI (passkeys)
http://api.localhost:9080 runner API
http://{slug}.localhost:9080 deployed apps

Deploy to Coolify

Point a Docker Compose resource at docker/compose.coolify.yaml (repo root, branch main). In Environment Variables, set the required BASE_DOMAIN (secrets auto-generate — just save); BETTER_AUTH_URL / GIT_PUBLIC_BASE derive from it unless overridden. Deploy: Coolify auto-provisions a generated domain for the caddy service and the runner serves the control UI on it (boot check). Traefik routes app./api./git. to caddy:80 and TCP-forwards *.<domain> SNI to caddy:443, where our Caddy terminates per-host TLS itself; tenant subdomains need zero per-app steps (full guide in apps/website/docs/deployment.mdx). Then paste the real hostnames once on the caddy service Domains field (Coolify can't take custom hostnames from Compose): https://app.<domain>:80,https://api.<domain>:80,https://git.<domain>:80. The apex stays on your marketing site.

Tenant subdomains (<slug>.<domain>) are fully automatic: a Traefik TCP router forwards every *.<domain> SNI straight to our Caddy on :443, and our Caddy mints a per-slug cert on demand (ask-gated by the runner — only live tenant/platform hosts get certs, no wildcard cert or DNS provider involved). Two one-time prerequisites: *.<domain> DNS → the server, and this file saved under Servers > server > Proxy > Dynamic Configurations (dashboard-pasted file config is static text — unlike compose labels, Coolify can't mangle it — and noite-tenants@docker resolves the TCP service the compose file defines):

tcp:
  routers:
    noite-tenants:
      entryPoints: [https]
      rule: 'HostSNIRegexp(`^.+\.<domain>$`)'
      service: noite-tenants@docker
      tls: { passthrough: true }

Then redeploy once and confirm the noite-tenants router in the Traefik dashboard. First visit to a new slug pauses a few seconds for issuance; certs persist in caddy-data. Fallback if passthrough misbehaves: add https://<slug>.<domain>:80 per app (exact hostnames use the plain HTTP challenge).

About

Tiny self-hostable PaaS for celld.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages