Skip to content
Merged

Prod #18

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Copy to ".env" at the repository root (Docker Compose and local tooling load it from here).
# Django also loads this file via backend/setrsoft/settings.py (repo root).

# Docker
DOCKER_NETWORK_NAME=proxy-tier
#Docker
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment header formatting is inconsistent with the rest of the file: #Docker is missing a space (# Docker).

Suggested change
#Docker
# Docker

Copilot uses AI. Check for mistakes.
DOCKER_NETWORK_NAME=default
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using default as the name for an external network is highly discouraged. In Docker Compose, default is a reserved name for the network automatically created for each project. Setting DOCKER_NETWORK_NAME=default for an external: true network will likely cause conflicts or lead to the container connecting to the wrong network. It is recommended to use a more descriptive name like proxy or traefik-public to avoid confusion with Docker's internal networking.

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env.example now sets DOCKER_NETWORK_NAME=default, but in docker-compose.prod.yml that value is used for an external network (public_gateway). Docker/Compose will not create an external network automatically, so using default is likely to fail unless a network with that exact name already exists. Consider reverting to a known Traefik network name (e.g. the previous proxy-tier) or updating the example/comment to instruct creating/choosing the correct external network name.

Suggested change
DOCKER_NETWORK_NAME=default
# Name of the pre-existing external Docker network shared with the reverse proxy (e.g. Traefik).
# Docker Compose will not create external networks automatically, so this must match an existing network.
DOCKER_NETWORK_NAME=proxy-tier

Copilot uses AI. Check for mistakes.

# --- PORTS & IP ---
SERVER_IP=setrsoft.com
Expand Down
52 changes: 29 additions & 23 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
# Production stack: Nginx (web) serves the Vite build and proxies /api/ and /admin/ to Gunicorn.
# Backend is not published on the host; only port 80 (web) is exposed.
#
# Usage: cp .env.example .env, set POSTGRES_PASSWORD and SECRET_KEY, then:
# docker compose -f docker-compose.prod.yml up -d --build
#
# Smoke checks (with stack up): curl -sSf http://localhost/
# curl -sSf http://localhost/api/health/ curl -sSf http://localhost/editor/ | head
services:
db:
image: postgres:16-alpine
env_file:
- .env
env_file: .env
environment:
POSTGRES_DB: ${POSTGRES_DB:-setrsoft}
POSTGRES_USER: ${POSTGRES_USER:-setrsoft}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env or the environment}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
volumes:
- postgres_data_prod:/var/lib/postgresql/data
healthcheck:
Expand All @@ -23,32 +14,31 @@ services:
timeout: 5s
retries: 5
networks:
- setrsoft_network
- internal_bridge

backend:
build:
context: ./backend
dockerfile: Dockerfile
expose:
- "${PORT_BACKEND:-8000}"
env_file:
- .env
env_file: .env
environment:
POSTGRES_HOST: db
POSTGRES_PORT: ${POSTGRES_PORT:-5432}
POSTGRES_DB: ${POSTGRES_DB:-setrsoft}
POSTGRES_USER: ${POSTGRES_USER:-setrsoft}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env or the environment}
SECRET_KEY: ${SECRET_KEY:?Set SECRET_KEY in .env or the environment}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
SECRET_KEY: ${SECRET_KEY:?Set SECRET_KEY}
DEBUG: ${DEBUG:-False}
ALLOWED_HOSTS: ${ALLOWED_HOSTS:?Set ALLOWED_HOSTS in .env}
ALLOWED_HOSTS: ${ALLOWED_HOSTS:?Set ALLOWED_HOSTS}
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://${SERVER_IP}}
TRUST_PROXY: ${TRUST_PROXY:-1}
depends_on:
db:
condition: service_healthy
networks:
- setrsoft_network
- internal_bridge

web:
build:
Expand All @@ -59,18 +49,34 @@ services:
- VITE_PUBLIC_POSTHOG_PROJECT_TOKEN=${VITE_PUBLIC_POSTHOG_PROJECT_TOKEN:-}
- VITE_PUBLIC_POSTHOG_HOST=${VITE_PUBLIC_POSTHOG_HOST:-}
container_name: setrsoft-frontend
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.docker.network=${DOCKER_NETWORK_NAME:-default}"
# HTTP → redirect HTTPS
- "traefik.http.routers.setrsoft-http.rule=Host(`setrsoft.com`) || Host(`www.setrsoft.com`)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The domain name is hardcoded, which limits the flexibility of the production configuration. It is better to use the ${SERVER_IP} variable defined in the environment to ensure consistency and easier deployment to different environments.

      - "traefik.http.routers.setrsoft-http.rule=Host(`${SERVER_IP}`) || Host(`www.${SERVER_IP}`)"

- "traefik.http.routers.setrsoft-http.entrypoints=http"
- "traefik.http.routers.setrsoft-http.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
# HTTPS
- "traefik.http.routers.setrsoft-https.rule=Host(`setrsoft.com`) || Host(`www.setrsoft.com`)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The domain name is hardcoded here as well. Using the ${SERVER_IP} variable is recommended for better maintainability and consistency across the configuration.

      - "traefik.http.routers.setrsoft-https.rule=Host(`${SERVER_IP}`) || Host(`www.${SERVER_IP}`)"

- "traefik.http.routers.setrsoft-https.entrypoints=https"
- "traefik.http.routers.setrsoft-https.tls=true"
- "traefik.http.routers.setrsoft-https.tls.certresolver=letsencrypt"
# Service
- "traefik.http.services.setrsoft-web.loadbalancer.server.port=80"
Comment on lines +62 to +67
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Traefik router labels don’t reference the explicitly defined service setrsoft-web, so traefik.http.services.setrsoft-web.loadbalancer.server.port=80 is likely unused and Traefik will fall back to its auto-generated service/port detection. Consider either (a) adding traefik.http.routers.<router>.service=setrsoft-web for both routers, or (b) renaming the service label to match the router/default service name so the configured port is actually applied.

Copilot uses AI. Check for mistakes.
depends_on:
- backend
networks:
- setrsoft_network
- proxy-tier
- internal_bridge
- public_gateway

volumes:
postgres_data_prod:

networks:
setrsoft_network:
internal_bridge:
driver: bridge
proxy-tier:
public_gateway:
external: true
name: ${DOCKER_NETWORK_NAME:-proxy-tier}
name: ${DOCKER_NETWORK_NAME:-default}
Loading