diff mbox series

[error-report-web,1/4] Run gunicorn behind nginx in docker-compose.

Message ID 20261018020116.75276-3-tim.orling@konsulko.com
State New
Headers show
Series Deploy with gunicorn behind nginx | expand

Commit Message

Tim Orling Oct. 18, 2026, 1:57 a.m. UTC
As in layerindex-web, nginx serves static files and proxies to
gunicorn. Unlike it, nginx sets X-Forwarded-Proto and accepts 10MB
reports. Drop the obsolete compose version key.

AI-Generated: Claude Opus 5.5
Signed-off-by: Tim Orling <tim.orling@konsulko.com>
---
 Dockerfile                 |  2 +-
 docker-compose.yaml        | 20 ++++++++++++----
 docker/nginx.conf          | 49 ++++++++++++++++++++++++++++++++++++++
 project/settings.py.docker |  3 ++-
 4 files changed, 68 insertions(+), 6 deletions(-)
 create mode 100644 docker/nginx.conf
diff mbox series

Patch

diff --git a/Dockerfile b/Dockerfile
index 94d55a6..b786a8c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,7 +10,7 @@  WORKDIR /app
 
 # Install dependencies
 COPY requirements.txt /app/
-RUN pip install -r requirements.txt
+RUN pip install -r requirements.txt gunicorn==26.2.0
 
 # Copy the project code into the container
 COPY . /app/
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 0291ff5..1fcb242 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,4 +1,3 @@ 
-version: '3'
 services:
   db:
     image: mysql:8.4
@@ -23,14 +22,27 @@  services:
       context: .
       dockerfile: Dockerfile
     container_name: error_report_web_backend
-    command: sh -c "python3 manage.py migrate --noinput && python3 manage.py runserver 0.0.0.0:8000"
+    command: sh -c "python3 manage.py migrate --noinput && python3 manage.py collectstatic --noinput && gunicorn project.wsgi --bind=:8000 --workers=4 --timeout=60"
     restart: always
-    ports:
-      - "8000:8000"
+    volumes:
+      - static:/app/staticfiles
     env_file:
       - .env
     depends_on:
       db:
          condition: service_healthy
+
+  web:
+    image: nginx:1.30
+    container_name: error_report_web_nginx
+    restart: always
+    ports:
+      - "8080:80"
+    volumes:
+      - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro
+      - static:/usr/share/nginx/html/static:ro
+    depends_on:
+      - backend
 volumes:
   data:
+  static:
diff --git a/docker/nginx.conf b/docker/nginx.conf
new file mode 100644
index 0000000..f1afcd4
--- /dev/null
+++ b/docker/nginx.conf
@@ -0,0 +1,49 @@ 
+# nginx in front of gunicorn, plain HTTP for local use. Production uses
+# nginx-ssl.conf.
+#
+# SPDX-License-Identifier: MIT
+
+error_log /dev/stdout info;
+worker_processes 1;
+pid /tmp/nginx.pid;
+
+events {
+    worker_connections 1024;
+    accept_mutex off;
+}
+
+http {
+    include mime.types;
+    default_type application/octet-stream;
+    access_log /dev/stdout combined;
+    sendfile on;
+
+    upstream app_server {
+        server backend:8000 fail_timeout=0;
+    }
+
+    server {
+        listen 80 default;
+        server_name _;
+        # Reports can be up to DATA_UPLOAD_MAX_MEMORY_SIZE (10MB).
+        client_max_body_size 10m;
+        keepalive_timeout 20;
+
+        # path for static files
+        root /usr/share/nginx/html;
+
+        location / {
+            try_files $uri @proxy_to_app;
+        }
+
+        location @proxy_to_app {
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            # Overwrites the client's value; Django trusts it.
+            proxy_set_header X-Forwarded-Proto $scheme;
+            proxy_set_header Host $http_host;
+            proxy_redirect off;
+
+            proxy_pass http://app_server;
+        }
+    }
+}
diff --git a/project/settings.py.docker b/project/settings.py.docker
index e1f9812..8cc9b7f 100644
--- a/project/settings.py.docker
+++ b/project/settings.py.docker
@@ -105,7 +105,8 @@  MEDIA_URL = ''
 # Don't put anything in this directory yourself; store your static files
 # in apps' "static/" subdirectories and in STATICFILES_DIRS.
 # Example: "/home/media/media.lawrence.com/static/"
-STATIC_ROOT = ''
+# Shared with nginx.
+STATIC_ROOT = '/app/staticfiles'
 
 # URL prefix for static files.
 # Example: "http://media.lawrence.com/static/"