generated from bisco/codex-bootstrap
feat: add optional letsencrypt tls
This commit is contained in:
+54
-13
@@ -18,16 +18,17 @@ Add `127.0.0.1 azionelab.org` to the local hosts file, then use
|
||||
`http://azionelab.org:8080`. NGINX binds to loopback port `8080` by default and routes
|
||||
the domain to Astro or Wagtail. Their direct loopback ports `4321` and `8000` remain
|
||||
available for diagnostics. PostgreSQL is available only on the Compose network.
|
||||
`postgres_data` and `media_data` are persistent named volumes.
|
||||
`postgres_data` and `media_data` are persistent named volumes. Local HTTPS is disabled
|
||||
by default; `letsencrypt_data` and `certbot_challenges` remain empty unless used.
|
||||
|
||||
The stack uses explicit PostgreSQL 16.9, Python 3.12.12, Node.js 22.20, and NGINX 1.30.0
|
||||
image versions. Containers are not privileged and use `no-new-privileges`.
|
||||
|
||||
Required runtime variables are `DATABASE_URL`, `DJANGO_SECRET_KEY`, `DJANGO_DEBUG`,
|
||||
`DJANGO_ALLOWED_HOSTS`, `WAGTAILADMIN_BASE_URL`, `PUBLIC_CMS_API_URL`,
|
||||
`NGINX_BIND_ADDRESS`, and `NGINX_HTTP_PORT`. PostgreSQL bootstrap variables are also
|
||||
documented in `.env.example`. Do not use the example credentials outside local
|
||||
development.
|
||||
`NGINX_BIND_ADDRESS`, `NGINX_HTTP_PORT`, and `NGINX_HTTPS_PORT`. The optional certificate
|
||||
variables and PostgreSQL bootstrap variables are documented in `.env.example`. Do not
|
||||
use the example credentials outside local development.
|
||||
|
||||
`WAGTAILADMIN_BASE_URL` must be browser-reachable because it is used for media URLs.
|
||||
`PUBLIC_CMS_API_URL` must be reachable by Astro; within Compose it is
|
||||
@@ -37,18 +38,58 @@ The PostgreSQL Compose service is `db`, so new `DATABASE_URL` values use `db:543
|
||||
The internal `postgres` alias is retained only for compatibility with existing local
|
||||
`.env` files and should not be used in new configuration.
|
||||
|
||||
The local virtual host is HTTP-only. Production must publish NGINX through the intended
|
||||
network interface, configure DNS for `azionelab.org`, terminate TLS, and prevent direct
|
||||
external access to application ports.
|
||||
## TLS deployment modes
|
||||
|
||||
When an external load balancer terminates TLS, leave `LETSENCRYPT_ENABLED=0`. The
|
||||
`certbot` service then has zero replicas and NGINX serves HTTP to the trusted internal
|
||||
network. Configure the load balancer to set `X-Forwarded-Proto: https`, keep application
|
||||
ports private, and restrict proxy access to the load balancer network.
|
||||
|
||||
For direct exposure, the HTTP-01 challenge requires public DNS for
|
||||
`LETSENCRYPT_DOMAIN` to resolve to this host and inbound TCP ports 80 and 443 to reach
|
||||
NGINX. Use a real operator address and production-safe Django settings:
|
||||
|
||||
```dotenv
|
||||
LETSENCRYPT_ENABLED=1
|
||||
LETSENCRYPT_DOMAIN=azionelab.org
|
||||
LETSENCRYPT_EMAIL=operator@example.org
|
||||
LETSENCRYPT_STAGING=1
|
||||
NGINX_BIND_ADDRESS=0.0.0.0
|
||||
NGINX_HTTP_PORT=80
|
||||
NGINX_HTTPS_PORT=443
|
||||
WAGTAILADMIN_BASE_URL=https://azionelab.org
|
||||
DJANGO_DEBUG=false
|
||||
DJANGO_ALLOWED_HOSTS=azionelab.org
|
||||
```
|
||||
|
||||
Then run `docker compose up --build -d` and inspect `docker compose logs certbot proxy`.
|
||||
NGINX serves HTTP until a certificate exists, then reloads and redirects normal HTTP
|
||||
requests to HTTPS. The ACME path remains available over HTTP for renewal.
|
||||
|
||||
Use the Let's Encrypt staging CA first. Before switching to the production CA, stop the
|
||||
stack and remove only the staging certificate volume after checking its exact Compose
|
||||
project name:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker volume ls --filter label=com.docker.compose.volume=letsencrypt_data
|
||||
docker volume rm PROJECT_letsencrypt_data
|
||||
```
|
||||
|
||||
Set `LETSENCRYPT_STAGING=0`, restart, and verify the certificate issuer in a browser or
|
||||
TLS inspection tool. Never use `docker compose down --volumes` on an environment whose
|
||||
database, media, or certificates must be retained.
|
||||
|
||||
## Production boundary
|
||||
|
||||
The Compose stack is a local development deployment. A public environment still needs
|
||||
TLS termination, production static/media serving, restricted admin access, managed
|
||||
secrets, off-host backups, monitoring, and an explicit domain/allowed-host policy.
|
||||
The Compose stack remains a minimal deployment base. A public environment still needs
|
||||
production static/media serving, restricted admin access, managed secrets, off-host
|
||||
backups, monitoring, firewall rules, and an explicit domain/allowed-host policy.
|
||||
|
||||
## Rollback
|
||||
|
||||
Revert the application commit and rebuild images. Keep the database and media volumes
|
||||
unless content deletion is intentional. Schema rollback must be evaluated per Django
|
||||
migration; take coordinated database and media backups first.
|
||||
Set `LETSENCRYPT_ENABLED=0` to disable the certificate service without deleting
|
||||
certificates, or terminate TLS at the load balancer. Revert the application commit and
|
||||
rebuild images for a full rollback. Keep database, media, and certificate volumes unless
|
||||
deletion is intentional. Schema rollback must be evaluated per Django migration; take
|
||||
coordinated database and media backups first.
|
||||
|
||||
Reference in New Issue
Block a user