Networking & TLS

One proxy, atomically reconfigured

Every app and service sits behind a single Sproobo-owned Caddy container. Route changes are validated before they're live, certificates renew themselves, and nothing about getting a domain working requires you to touch the box directly.

The shared proxy

A server gets one ingress container that owns :80 and :443. Route changes go through Caddy's admin API as an atomic config load, and caddy validate gates every change — a bad route can't leave the box half-broken. The last-known-good config is retained, so a failed reload falls back to what was already working rather than an empty proxy.

Caddy reaches your app or service by its container DNS name over the internal network, not a host port — see the internal network below.

Every domain is apex + www, always

Give an app example.com and Sproobo always configures both example.com and www.example.com — never just one. This isn't a checkbox: a validator rejects any route where an apex domain lacks its www counterpart or vice versa. Both names populate the Caddy site address and the certificate's SAN set, and a canonical 301 redirects the non-preferred host to the one you picked — both stay served and certificate-covered so the redirect itself never breaks.

Four ways to get a certificate

TLS mode is a per-app setting (dashboard Settings tab, or buildSpec.tlsMode under the hood) — pick the one that matches how the server is reachable:

01

Default — HTTP-01

The normal path. Caddy obtains and renews the certificate itself, straight from the route's site addresses, with no extra setup on your side. Use this unless the server can't open :80/:443 to the public internet or sits behind your own load balancer.

02

Delegated DNS-01

For a VPN-only, LAN-only, or otherwise firewalled server that can never complete an HTTP-01 challenge from the public internet. Switching an app to this mode gives you one CNAME to create at your own DNS provider — _acme-challenge.<apex> pointing at a label under acme.sproobo.com — and nothing else. Sproobo checks it against two independent public resolvers every few minutes (or on-demand with a "Verify now" button); once it matches, Caddy issues an apex-plus-wildcard certificate through that delegation and the app redeploys automatically. No DNS-provider API keys or credentials are ever collected — just the one CNAME record.

03

Wildcard — one cert, many subdomains

For a fleet of subdomains under one apex (a multi-tenant staging pattern, say). Verify a single CNAME once against a standalone WildcardDomain and Sproobo issues one *.apex certificate. Every app you then create as a child of that domain shares the same cert automatically — zero additional DNS records and zero additional certificate issuance per subdomain. It's chosen at app-creation time; changing an existing app into or out of wildcard mode isn't supported from the dashboard yet.

04

External termination

For an app already sitting behind your own TLS-terminating load balancer or CDN (an AWS ALB, a Cloudflare proxy). Sproobo skips certificate issuance entirely and serves the app over plain HTTP on :80 — with no HTTP→HTTPS redirect for that host, since your LB is the one that owns TLS and forwards plain HTTP downstream.

HTTP upgrades to HTTPS, automatically

Outside of the external-termination case above, every host gets Caddy's automatic HTTPS behavior platform-wide: plain HTTP requests get a redirect to HTTPS by default, with no per-app opt-in required. Switching an app to external mode is the one deliberate way to turn that off for a given host, since in that mode Sproobo isn't the one terminating TLS in the first place.

The internal network: sproobo-int

Every app and service container attaches to one internal Docker network, sproobo-int. The proxy reaches an app's upstream, and an app reaches a bound service, purely by container DNS name on that network — no host ports are published for any of it. The only container published to 0.0.0.0 on the box is the proxy container itself, on :80/:443; everything else is unreachable from outside the box even if the host firewall were wide open.

The firewall that doesn't lie

The host-level firewall is ufw on Debian-family servers and firewalld on RHEL-family — but the part that actually enforces a rule against a Docker-published port is the DOCKER-USER iptables/nftables chain, since Docker's own NAT rules run ahead of the host firewall and would otherwise bypass it silently. Sproobo reconciles declared rules into DOCKER-USER as well, so a deny is actually effective rather than a rule that looks right in ufw status while the port stays reachable underneath.

The dashboard's firewall page reflects this directly: it merges what you've declared against what the agent's latest fact snapshot actually observed on the host, and surfaces any observed rule with no declared counterpart as drift — a rule the box picked up outside Sproobo.

A foreign proxy never gets fought for the port.

Bootstrap checks who already holds :80/:443 before starting the Sproobo proxy. If something else already owns them — your own nginx, say — bootstrap skips starting sproobo-proxy and tells you to point your existing proxy at the sproobo-int network instead, rather than starting a second process to fight over the same port.

Next

  • Read the full trust-boundary and firewall lockout-prevention model on Security model.
  • See where TLS mode fits into creating and configuring an app on Apps & deploys.
  • Walk the full enroll-to-deploy flow on Getting started.