Skip to content

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:

Terminal window
cd apps/vaultwarden
cp .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.local
docker compose -f docker-compose.local.yml --env-file .env.local up -d

Each 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:

Terminal window
ls apps/<name>/docker-compose.local.yml

Eight 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.

Traefik — routing and TLS →

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 →

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. 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.

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.