diff mbox series

[layerindex-web,RFC,2/2] docker-compose.nginx-proxy.yml: add a production setup using nginx-proxy

Message ID 20261114005006.28193-3-tim.orling@konsulko.com
State New
Headers show
Series uWSGI, and a docker-compose setup with nginx-proxy | expand

Commit Message

Tim Orling Nov. 14, 2026, 12:50 a.m. UTC
Add docker-compose.nginx-proxy.yml, which uses nginx-proxy and
acme-companion in place of the layersweb and layerscertbot containers
of docker-compose.prod.yml: the Let's Encrypt certificate is obtained
and renewed automatically, without a hand-edited nginx configuration,
and layersapp serves the static files. The host name and email address
go in .env.prod.proxy.

AI-Generated: Claude Opus 5.5
Signed-off-by: Tim Orling <tim.orling@konsulko.com>
---
 .env.prod.proxy                |  7 +++
 README.md                      | 12 +++++
 docker-compose.nginx-proxy.yml | 90 ++++++++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+)
 create mode 100644 .env.prod.proxy
 create mode 100644 docker-compose.nginx-proxy.yml
diff mbox series

Patch

diff --git a/.env.prod.proxy b/.env.prod.proxy
new file mode 100644
index 0000000..1350f27
--- /dev/null
+++ b/.env.prod.proxy
@@ -0,0 +1,7 @@ 
+# For docker-compose.nginx-proxy.yml. Set real values in a local commit on the
+# server; never push them.
+# The site's host name, for nginx-proxy and for the certificate
+#VIRTUAL_HOST=<set this here>
+#LETSENCRYPT_HOST=<set this here>
+# The email address Let's Encrypt sends expiry notices to
+#LETSENCRYPT_EMAIL=<set this here>
diff --git a/README.md b/README.md
index a1f6087..c912034 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,18 @@  credentials, email server, `DEBUG`) from `.env.<context>.<service>` files, where
   docker compose -f docker-compose.prod.yml up -d --build
   ```
 
+- `docker-compose.nginx-proxy.yml` is a production setup that uses
+  [nginx-proxy](https://github.com/nginx-proxy/nginx-proxy) and
+  [acme-companion](https://github.com/nginx-proxy/acme-companion) in place of
+  the `layersweb` and `layerscertbot` containers: the certificate is obtained
+  and renewed automatically, and `layersapp` serves the static files. It uses
+  the `.env.prod.*` files, and `.env.prod.proxy` for the host name and the
+  email address for Let's Encrypt:
+
+  ```sh
+  docker compose -f docker-compose.nginx-proxy.yml up -d --build
+  ```
+
 The `.env*` files are excluded from the Docker images.
 
 Other settings the app and celery files can set:
diff --git a/docker-compose.nginx-proxy.yml b/docker-compose.nginx-proxy.yml
new file mode 100644
index 0000000..b9deb44
--- /dev/null
+++ b/docker-compose.nginx-proxy.yml
@@ -0,0 +1,90 @@ 
+# Production setup with nginx-proxy and acme-companion in place of the
+# layersweb (nginx) and layerscertbot containers of docker-compose.prod.yml:
+# nginx-proxy forwards requests to layersapp, which also serves the static
+# files, and acme-companion gets and renews its Let's Encrypt certificate.
+# Set the host name and email in .env.prod.proxy (see README.md).
+services:
+  layersdb:
+    image: mariadb:lts
+    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --wait_timeout=28800 --max_allowed_packet=128M
+    volumes:
+      - mariadb_data:/var/lib/mysql
+    env_file:
+     - ".env.prod.db"
+    restart: unless-stopped
+
+  layersapp:
+    depends_on:
+      - layersdb
+    build: .
+    image: halstead/layerindex-app
+    hostname: localhost
+    volumes:
+     - layersmeta:/opt/workdir
+     - logvolume:/opt/layerindex-task-logs
+    env_file:
+     - ".env.prod.app"
+     # Only layersapp may read this file: nginx-proxy forwards requests to
+     # every container that has VIRTUAL_HOST set
+     - ".env.prod.proxy"
+    environment:
+      VIRTUAL_PORT: 5000
+    restart: unless-stopped
+
+  layersproxy:
+    depends_on:
+      - layersapp
+    image: nginxproxy/nginx-proxy:1.9-alpine
+    container_name: layersproxy
+    ports:
+     - "443:443"
+     - "80:80"
+    volumes:
+     - certs:/etc/nginx/certs
+     - acmehtml:/usr/share/nginx/html
+     - vhost:/etc/nginx/vhost.d
+     - /var/run/docker.sock:/tmp/docker.sock:ro
+    restart: unless-stopped
+
+  layersacme:
+    depends_on:
+      - layersproxy
+    image: nginxproxy/acme-companion
+    environment:
+      NGINX_PROXY_CONTAINER: layersproxy
+    volumes:
+     - /var/run/docker.sock:/var/run/docker.sock:ro
+     - certs:/etc/nginx/certs
+     - acmehtml:/usr/share/nginx/html
+     - vhost:/etc/nginx/vhost.d
+     - acme:/etc/acme.sh
+    restart: unless-stopped
+
+  layersrabbit:
+    image: rabbitmq:alpine
+    restart: unless-stopped
+    env_file:
+     - ".env.prod.rabbit"
+
+  layerscelery:
+    depends_on:
+      - layersdb
+      - layersapp
+      - layersrabbit
+    image: halstead/layerindex-app
+    volumes:
+     - layersmeta:/opt/workdir
+     - logvolume:/opt/layerindex-task-logs
+    env_file:
+     - ".env.prod.celery"
+    restart: unless-stopped
+    command: /usr/local/bin/celery --workdir=/opt/layerindex --app layerindex.tasks worker --loglevel=INFO
+
+volumes:
+  layersmeta:
+  mariadb_data:
+  logvolume:
+  certs:
+  acmehtml:
+  vhost:
+  acme: