HTTPS with Caddy¶
How to put Caddy in front of a single-host installation so users reach LogsTotal over HTTPS.
A fleet sets the same thing up through DEPLOY_DOMAIN — see
Fleet installation.
HTTPS is the proxy Compose profile: Caddy in front of the app, obtaining and renewing its
certificate by itself. Every command on this page runs on the host, from the install
directory.
Choose a TLS mode¶
PROXY_TLS decides how Caddy gets its certificate, and the rest follows from it:
PROXY_TLS |
Use when | What Caddy does | Needs | COOKIE_INSECURE |
ENABLE_HSTS |
|---|---|---|---|---|---|
acme (default) |
The instance is on the internet under a public name | Fetches a certificate from Let's Encrypt and renews it | A public DNS record for DOMAIN pointing at the host; ports 80 and 443 reachable from the internet; ACME_EMAIL |
false |
true |
internal |
An internal or air-gapped network | Issues a certificate from its own certificate authority | Nothing: no DNS, no port 80, no internet. Clients must trust Caddy's root — see Air-gapped and internal networks | false |
false |
custom |
Your organisation already issues certificates | Serves your certificate and key from ./certs |
The certificate and key files — see Your own certificate | false |
true |
off |
A load balancer or another proxy already terminates TLS in front of this one | Serves plain HTTP | Something in front that terminates TLS | true |
false |
COOKIE_INSECURE and ENABLE_HSTS must match the mode. When they do not, login fails
without an error message. The commands below set all of them together.
DOMAIN must be a name or an IPv4 address; an IPv6 literal does not work (see
Known limitations). For internal, any name works, including
one that only your DNS or an /etc/hosts entry resolves.
Turn it on¶
On a new host, from the very first command:
DOMAIN=logs.example.com ACME_EMAIL=admin@example.com ./logstotal quickstart # acme
DOMAIN=logs.internal QUICKSTART_TLS=internal ./logstotal quickstart # internal
On an installation that is already running:
DOMAIN=logs.example.com ACME_EMAIL=admin@example.com ./logstotal proxy:enable # acme
DOMAIN=logs.internal ./logstotal proxy:enable -- --tls internal # internal
DOMAIN=logs.example.com ./logstotal proxy:enable -- --tls custom # custom
DOMAIN=logs.example.com ./logstotal proxy:enable -- --tls off # off
./logstotal docker:up
QUICKSTART_TLS accepts the same four modes as --tls. Both commands write the same seven
.env keys: COMPOSE_PROFILES (keeping any other profiles), DOMAIN, PROXY_TLS,
ACME_EMAIL, WEB_PORT=127.0.0.1:8000:8000 so the app itself is no longer reachable from
outside, and the COOKIE_INSECURE and ENABLE_HSTS pair for the mode. ./logstotal proxy:enable
is safe to re-run, and for acme it checks that DOMAIN resolves to this host (a warning,
never a failure).
For acme, Caddy gets its certificate on first start. Until it has one, the site does not
load over HTTPS; follow it with ./logstotal docker:logs:proxy.
Behind Caddy, every request reaches the app from Caddy's address. For rate limits and the
activity log to see real client addresses, set TRUST_PROXY_HEADERS and
TRUSTED_PROXY_CIDRS — see Rate limit semantics.
Your own certificate¶
With PROXY_TLS=custom:
- Put the certificate (full chain) and the key in
certs/in the install directory. That directory is mounted into Caddy at/etc/caddy/certsand is never included in a release archive. - Point
.envat them by their path inside the container:
PROXY_TLS_CERT=/etc/caddy/certs/fullchain.pem
PROXY_TLS_KEY=/etc/caddy/certs/privkey.pem
./logstotal docker:up
Both keys are required, and both paths must be under /etc/caddy/certs. Caddy refuses to
start otherwise, and says which file is wrong.
Air-gapped and internal networks¶
With PROXY_TLS=internal, Caddy runs its own certificate authority, so there is nothing to
validate and nothing to renew from outside. One manual step remains: the machines that use
LogsTotal must trust Caddy's root certificate. Copy it out:
docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt ./logstotal-root.crt
and install logstotal-root.crt as a trusted root on those machines. Until you do, every
browser shows a certificate warning. On the machine that runs ./logstotal deploy:smoke,
either install it too or pass SMOKE_CACERT=./logstotal-root.crt: without either, the smoke
check will not send basic-auth credentials over the unverified connection. That is why ENABLE_HSTS stays false in this mode:
HSTS turns a warning you can click through into a page that cannot be opened at all.
HTTP basic auth¶
Caddy can ask for a user name and password before anything reaches the app, in every mode,
including off. Generate the hash as described in
Generating secrets and keys, then either
set BASIC_AUTH_USER and BASIC_AUTH_HASH in .env (the hash in single quotes), or let
proxy:enable write them:
DOMAIN=logs.example.com ACME_EMAIL=admin@example.com \
./logstotal proxy:enable -- --basic-auth-user ops --basic-auth-hash '<bcrypt hash>'
API tokens cannot authenticate through basic auth — see Known limitations.
Turn HTTPS off¶
Plain HTTP is the default and needs nothing: leave proxy out of COMPOSE_PROFILES, keep
COOKIE_INSECURE=true, and the app serves on port 8000. That is the state
./logstotal quickstart leaves without DOMAIN.
To remove a proxy you enabled, change these four keys in .env:
COMPOSE_PROFILES= # remove `proxy` (keep any other profiles)
WEB_PORT=8000:8000 # publish the app itself again
COOKIE_INSECURE=true # plain HTTP drops a Secure cookie
ENABLE_HSTS=false
Then run ./logstotal docker:up.
Important
PROXY_TLS=off does not remove Caddy. It keeps Caddy in front and serves plain HTTP
through it, for when something else already terminates TLS. Removing the proxy profile
is what takes Caddy away.