What every server needs
Two things here are installed once on a host and then shared by everything that runs on it. They do not have the same standing, and treating them as if they did is how a server ends up with an intrusion detector and no working certificates.
First — a server is not needed to try an application
Section titled “First — a server is not needed to try an application”Most stacks ship a docker-compose.local.yml next to the production one. It
publishes the application on 127.0.0.1, reads plain values from .env.local,
and involves no proxy, no DNS and no certificate:
cd apps/vaultwardencp .env.local.example .env.local# the secrets ship empty; generate the ones this stack needs{ echo "DB_PASSWORD=$(openssl rand -hex 32)" echo "DB_ROOT_PASSWORD=$(openssl rand -hex 32)"} >> .env.localdocker compose -f docker-compose.local.yml --env-file .env.local up -dEach guide’s Try it locally block, and each file’s header, carries the commands for that stack — the secrets it needs, and for some a directory created or its ownership set first. Check whether the one you want has the file:
ls apps/<name>/docker-compose.local.ymlEight stacks have none, because running them alone shows nothing: adminer,
traefik, acme-certs, dnsmasq, crowdsec, hawser, portainer-agent and
beszel-agent.
The rest of this page is for serving an application to someone other than yourself.
A reverse proxy — for anything you serve
Section titled “A reverse proxy — for anything you serve”Without one, nothing here is reachable over the network. That is what this half
of the page is about; a stack you only run for yourself needs none of it. Web
applications publish no ports of their own. Each attaches to its own edge network
(<project>-edge, which the proxy joins through EDGE_NETWORKS) and expects something in front to terminate TLS and route requests
by hostname. That job — the reverse proxy — is what every served application needs.
Traefik is what this blueprint provides for it, and it also carries the middleware each service switches on: security headers, rate limits, and the access rules that decide whether a service answers the open internet or only your VPN. Another reverse proxy could do the routing, but the middleware, the certificate handling and the per-application labels here are written for Traefik — swapping it means replacing those, and the blueprint does not maintain a second set.
One certificate strategy, one set of access policies, one place to change them.
CrowdSec — a layer you add
Section titled “CrowdSec — a layer you add”Optional, and worth adding once something is public. It reads Traefik’s access log, decides which addresses are attacking, and — through enforcement you install separately — blocks them, either inside the proxy or at the host firewall.
The scoping matters more here than the feature list. Behind a VPN-only access policy CrowdSec sees almost nothing: 120 requests against a VPN-only host move its counter by about three, and none of them got through either. It earns its place the moment a service faces the open internet, and not much before.
CrowdSec — intrusion detection →
Set up once, used by everything after
Section titled “Set up once, used by everything after”Dockhand manages Compose stacks from a Git repository and reaches the Docker daemon through a filtered proxy rather than the socket. Anything that manages Docker is equivalent to root on the host, which is why it ships VPN-only.
Portainer is a web interface onto the same daemon — containers, logs, volumes, images — through a filtering socket proxy of its own, VPN-only as well.
dnsmasq answers names on your own network — wildcard zones, static records for machines with no DNS entry, and a cache in front of an upstream resolver. It runs on host networking and is not behind the proxy, because it is the name layer the proxy relies on.
Headscale is a control server of your own for
Tailscale clients — the tailnet that access-vpn admits, run by you.
Certificates for devices behind no proxy issues and renews TLS certificates for the things Traefik never sees — a NAS, a router, a mail server.
Single sign-on — optional, server-wide
Section titled “Single sign-on — optional, server-wide”Single sign-on. Three providers, and they are alternatives. Authentik is set up once and then attached per route as a proxy middleware, so several applications can share one login even when they have none of their own; five containers, and it needs SMTP, a database and Redis. Keycloak is the provider most applications that speak OpenID Connect or SAML were tested against, in two containers, with no proxy of its own. Authelia is a login portal with two factors that Traefik asks through forward-auth, with users and rules in files. Each is a service to operate, not a setting to switch on; Choosing between services sets them side by side.
Server-wide, and covered elsewhere
Section titled “Server-wide, and covered elsewhere”Document editing. OnlyOffice is also set up once and shared by several applications, which makes it look like it belongs here. It does not: nothing breaks without it, and it exists for the applications that embed it. It sits with them — OnlyOffice.
Backups. Server-wide, but a different job with its own failure modes, and it has its own section — backup and restore.
Monitoring and alerting. Uptime checks, a dead-man’s switch, host metrics and the receiver the alerts go to have their own section — what each monitoring stack catches.
Where to go from here
Section titled “Where to go from here”- Nothing running yet: preparing a server covers the host decisions that are expensive to reverse, then hands over to Traefik.
- Traefik up and verified: pick a service under applications, or add CrowdSec if anything will be public.
- Want the shape of the whole thing first: how a server fits together.
© 2026 SecDockBlueCode Apache-2.0Site content CC BY-NC 4.0