Files
nuc/docs/ecija-intranet-deployment.md
Alejandro Gutiérrez 8b503a549c Add operational documentation
CloudBeaver database manager guide, Ecija intranet deployment,
Gitea-Coolify auto-deploy and integration docs, monitoring setup
with presentation, remote access guide, security architecture,
and Turbostarter deployment procedure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:17:18 +01:00

25 KiB

ECIJA Intranet - NUC Deployment Guide

Purpose: Step-by-step deployment of the ECIJA Intranet Django backend on the NUC home server. Source project: /Users/agutierrez/Desktop/ECIJA-Intranet/ Database dump: /Users/agutierrez/Desktop/flexicar-intranet-backend/dump-flexicar-202602071346-backup/ Credentials file: /Users/agutierrez/Desktop/flexicar-intranet-backend/windy-shoreline-225910-efd33901e56c.json Installation reference: /Users/agutierrez/Desktop/flexicar-intranet-backend/instalacion_intra.docx


Architecture Overview

                    ┌─────────────────────────────────────────────┐
                    │              NUC Server (192.168.1.3)       │
                    │              Tailscale: 100.113.153.45      │
                    │                                             │
 intranet.nuc.lan   │  ┌──────────┐    ┌────────────────────┐    │
 ──────────────────►│  │ Traefik  │───►│  Django/Gunicorn   │    │
                    │  │ :80/:443 │    │  :8010             │    │
                    │  └──────────┘    └────────┬───────────┘    │
                    │                           │                 │
                    │                  ┌────────┼───────┐        │
                    │                  │        │       │        │
                    │           ┌──────▼──┐ ┌───▼───┐  │        │
                    │           │Postgres │ │ Redis │  │        │
                    │           │  :5434  │ │ :6379 │  │        │
                    │           └─────────┘ └───────┘  │        │
                    │                                   │        │
                    │           ┌──────────────────────┐│        │
                    │           │  Background Worker   ││        │
                    │           │  (process_tasks)     ││        │
                    │           └──────────────────────┘│        │
                    └───────────────────────────────────┼────────┘
                                                        │
                                                        ▼
                                              ┌──────────────────┐
                                              │  Google Cloud    │
                                              │  Storage (GCS)   │
                                              │  Bucket:         │
                                              │  ecija-intranet  │
                                              └──────────────────┘

Design principle: change nothing, only deploy

The NUC is the compute host only. All external integrations (GCS, HubSpot, Azure AD, 3G, reCAPTCHA) stay exactly as the original app expects them. This means:

  • Zero code changes to settings.py
  • Same env vars as the official installation doc
  • Same GCS bucket and credentials
  • If it works locally on a Mac, it works on the NUC

What the NUC already provides (no setup needed)

Service Port Notes
Redis 6379 UUID: vkg44cgcss4ococgk0cs000o - reuse existing
Traefik 80/443 Reverse proxy for intranet.nuc.lan
n8n 5678 Workflow automation (intranet has n8n webhooks)
Adminer 8088 DB admin UI
CloudBeaver 8978 DB admin UI (alternative)
Dozzle 9999 Container log viewer
Uptime Kuma 3001 Service monitoring
Kopia 51515 Backup management

What needs to be deployed

Service Port Method
PostgreSQL (dedicated) 5434 Coolify service
Django + Gunicorn 8010 Coolify docker-compose
Background Worker - Same image, different command

Phase 1: PostgreSQL Database

1.1 Deploy a dedicated PostgreSQL instance via Coolify

Use port 5434 to avoid conflicts with existing Postgres instances (5432, 5433, 5442 already in use).

# Via Coolify MCP:
mcp__coolify__service(
  action="create",
  type="postgresql",
  name="ecija-intranet-db",
  server_uuid="qk84w0goo4w48g4ggsoo0oss",
  project_uuid="a8484ggc88c40w4g4k004ow0",
  environment_name="production",
  instant_deploy=True
)

If native type fails, deploy via docker-compose:

services:
  ecija-postgres:
    image: postgres:17
    container_name: ecija-intranet-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: flexicar-prod
    ports:
      - "5434:5432"
    volumes:
      - ecija_pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d flexicar-prod"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  ecija_pgdata:

1.2 Enable required PostgreSQL extensions

ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c 'CREATE EXTENSION IF NOT EXISTS pg_trgm;'"
ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c 'CREATE EXTENSION IF NOT EXISTS unaccent;'"

1.3 Transfer and restore the database dump

The dump is in pg_dump directory format (4 GB, 453 tables).

Important: The dump was exported from a database called flexicar with owner flexicar, but the Django app expects DB_NAME=flexicar-prod with DATABASE_USER=postgres (per the official install doc). We restore into flexicar-prod using --no-owner so all objects are owned by postgres.

# Step 1: Transfer dump directory to NUC
scp -r /Users/agutierrez/Desktop/flexicar-intranet-backend/dump-flexicar-202602071346-backup/ nuc:/tmp/ecija-dump/

# Step 2: Copy dump into the Postgres container
ssh nuc "docker cp /tmp/ecija-dump/ ecija-intranet-db:/tmp/ecija-dump/"

# Step 3: Restore the dump into flexicar-prod (ignore original ownership)
ssh nuc "docker exec ecija-intranet-db pg_restore \
  -U postgres \
  -d flexicar-prod \
  --no-owner \
  --no-privileges \
  --jobs=4 \
  /tmp/ecija-dump/"

# Step 4: Verify table count (should be ~453)
ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c \"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';\""

# Step 5: Clean up dump from container
ssh nuc "docker exec ecija-intranet-db rm -rf /tmp/ecija-dump/"

# Step 6: Clean up dump from NUC host
ssh nuc "rm -rf /tmp/ecija-dump/"

1.4 Verify database is accessible

# From NUC
ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c 'SELECT current_database(), current_user;'"

# From Adminer (browser)
# URL: http://192.168.1.3:8088
# System: PostgreSQL
# Server: ecija-intranet-db:5432  (or 192.168.1.3:5434 if external)
# Username: postgres
# Password: postgres
# Database: flexicar-prod

Phase 2: Transfer Project Files

2.1 Transfer the project to NUC

# Exclude unnecessary files (venv, node_modules, caches)
rsync -avz --progress \
  --exclude '.git' \
  --exclude 'env/' \
  --exclude 'node_modules/' \
  --exclude '.DS_Store' \
  --exclude '__pycache__' \
  --exclude '*.pyc' \
  /Users/agutierrez/Desktop/ECIJA-Intranet/ \
  nuc:/opt/ecija-intranet/

2.2 Transfer the Google Cloud Storage credentials

The GCS service account key is required for static/media file storage. This is the same file used in the original production environment.

Important: The official install doc sets GS_CREDENTIALS="/secrets/bucket/credentials.json" (generic name), while GS_CREDENTIALS_LOCAL uses the full filename. We mount the credentials dir to /secrets/bucket/ and copy the file as credentials.json to match the production GS_CREDENTIALS path.

# Create the credentials directory on NUC
ssh nuc "mkdir -p /opt/ecija-intranet/config/credenciales"

# Transfer the service account JSON (keep original name for GS_CREDENTIALS_LOCAL)
scp /Users/agutierrez/Desktop/flexicar-intranet-backend/windy-shoreline-225910-efd33901e56c.json \
  nuc:/opt/ecija-intranet/config/credenciales/windy-shoreline-225910-efd33901e56c.json

# Also copy as credentials.json (for GS_CREDENTIALS=/secrets/bucket/credentials.json)
ssh nuc "cp /opt/ecija-intranet/config/credenciales/windy-shoreline-225910-efd33901e56c.json \
  /opt/ecija-intranet/config/credenciales/credentials.json"

2.3 Optionally push to NUC's Gitea for version control

# From local machine
cd /Users/agutierrez/Desktop/ECIJA-Intranet

# Create the repo in Gitea first (via browser at http://192.168.1.3:3030 or API)
# Then add remote and push
git remote add nuc http://192.168.1.3:3030/alezmad/ecija-intranet.git
git push nuc main

Phase 3: Django Application Container

3.1 Create the production Dockerfile

The existing Dockerfile uses gcr.io/google_appengine/python (Google App Engine base image) which is unnecessary on the NUC.

Create file on NUC at /opt/ecija-intranet/Dockerfile.nuc:

FROM python:3.9-slim

# System dependencies for psycopg2, Pillow, lxml, pdf libs
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libxml2-dev \
    libxslt1-dev \
    zlib1g-dev \
    libffi-dev \
    libmupdf-dev \
    git \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Install Node.js 18 for frontend build
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
    && apt-get install -y nodejs \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Python dependencies (root level - includes all packages)
COPY requirements.txt /app/requirements.txt
RUN pip install --upgrade pip && \
    pip install --use-pep517 -r /app/requirements.txt

# Copy application code
COPY app/ /app/

# Frontend dependencies and build
RUN npm install && npm run build

EXPOSE 8010

# Entrypoint script
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["gunicorn", "ecija.wsgi:application", \
     "--workers", "4", \
     "--threads", "4", \
     "--timeout", "120", \
     "--graceful-timeout", "30", \
     "--keep-alive", "5", \
     "--max-requests", "1000", \
     "--max-requests-jitter", "100", \
     "--bind", "0.0.0.0:8010", \
     "--access-logfile", "-", \
     "--error-logfile", "-", \
     "--log-level", "info"]

3.2 Create the entrypoint script

Create file on NUC at /opt/ecija-intranet/docker-entrypoint.sh:

#!/bin/bash
set -e

echo "Running database migrations..."
python manage.py migrate --noinput

echo "Collecting static files..."
python manage.py collectstatic --noinput || true

echo "Starting application..."
exec "$@"

3.3 Deploy via Coolify docker-compose

Deploy through Coolify MCP using docker_compose_raw:

services:
  ecija-web:
    build:
      context: /opt/ecija-intranet
      dockerfile: Dockerfile.nuc
    container_name: ecija-intranet-web
    restart: unless-stopped
    ports:
      - "8010:8010"
    env_file:
      - /opt/ecija-intranet/.env.production
    volumes:
      - ecija_media:/app/media
      - /opt/ecija-intranet/config/credenciales:/secrets/bucket:ro
    networks:
      - ecija-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8010/"]
      interval: 30s
      timeout: 10s
      retries: 3

  ecija-worker:
    build:
      context: /opt/ecija-intranet
      dockerfile: Dockerfile.nuc
    container_name: ecija-intranet-worker
    restart: unless-stopped
    command: ["python", "manage.py", "process_tasks"]
    env_file:
      - /opt/ecija-intranet/.env.production
    volumes:
      - ecija_media:/app/media
      - /opt/ecija-intranet/config/credenciales:/secrets/bucket:ro
    networks:
      - ecija-network

volumes:
  ecija_media:

networks:
  ecija-network:
    driver: bridge

Phase 4: Environment Configuration

4.1 Create the production env file

This uses the same values from the official installation doc (instalacion_intra.docx), with only the DB host changed to point to the NUC container.

Create on NUC at /opt/ecija-intranet/.env.production:

# ============================================================
# ECIJA Intranet - NUC Production Environment
# Source: /Users/agutierrez/Desktop/flexicar-intranet-backend/instalacion_intra.docx
# Principle: IDENTICAL to official install doc, only DB_HOST changed
# ============================================================
# Lines below are a 1:1 copy from the docx. Only DB_HOST differs
# (container name instead of 127.0.0.1).
# Do NOT add extra vars that aren't in the original doc.

DATABASE_USER=postgres
DATABASE_PASSWORD=postgres
DJANGO_ENVIRONMENT=local
MS_SECRET_KEY=H6CLoLV?kO.uaZOI3lkiHv=L:jWqk12t
G_RECAPTCHA_SECRET_KEY=
G_RECAPTCHA_SITE_KEY=
DEBUG=True
DEMO=False
GKE_ENABLED=True
AG=False
GS=True
RECAPTCHA=True
DB_NAME_DEMO=ecija_demo
DB_NAME=flexicar-prod
DB_HOST=ecija-intranet-db
DB_PORT=5432
STATIC_URL=/static/
MEDIA_URL=/media/
GS_BUCKET_NAME=ecija-intranet
GS_PROJECT_ID=ecija-intranet
GS_CREDENTIALS=/secrets/bucket/credentials.json
TENANT_ID=8e39b277-105b-4b2e-b21c-e06cd806a070
CLIENT_ID=6858acbb-098f-4261-987d-6a9892b06d86
RELYING_PARTY_ID=api://18d841fc-2054-4b76-992a-2a11fc9ade78
AUDIENCE=api://18d841fc-2054-4b76-992a-2a11fc9ade78
CODIGO_EMPRESA_3G=ecijades
LOGIN_3G=ws64304
PASSWORD_3G=z4JcpcTqjd
IDIOMA_3G=ES
API_KEY_3G=m23o2MHszn128snAkwkaAKnzLK29s91klalzl19Jjkzj19zlxlnwZNmMnN1812nznNMjsjz9MnznWwqsjzlSAK2znqh0z9134
APPS_INSTALADAS=django_auth_adfs,ecija,meta_aepd_app,agile,gestor_licencias,telefonia,administracion,certificados3g,ticketing,analytics,demo,gestion_documental,sp_widgets,configuracion_interfaz,widgets_dashboard,historico,comentarios,avisos,procesal,canal_denuncias,referidos,notificacion,busqueda_google,tareas,bank_movements,notas,oportunidades,reclamaciones,nux,eventos,fleximanage,comunicaciones
HUBSPOT_API_URL_COMPANIES=https://api.hubapi.com/companies/v2/companies
HUBSPOT_API_URL_CONTACTS=https://api.hubapi.com/contacts/v1/contact
HUBSPOT_API_KEY=pat-eu1-5b7e6860-e273-4a92-b6f8-b6eead476505
GS_CREDENTIALS_LOCAL=/secrets/bucket/windy-shoreline-225910-efd33901e56c.json
GOOGLE_APPLICATION_CREDENTIALS=/secrets/bucket/windy-shoreline-225910-efd33901e56c.json

4.2 Alignment with official install doc

The .env.production file is a 1:1 copy of the env vars from instalacion_intra.docx with one change:

Variable Official doc NUC deployment Why
DB_HOST 127.0.0.1 ecija-intranet-db Container networking (Docker DNS resolves container names)

Everything else is identical. GCS stays because:

  • The app is built on django-storages[google], not S3
  • The credentials already work
  • Zero code changes required
  • Same config carries over to real production

Phase 5: Networking & DNS

5.1 Add DNS entry for intranet.nuc.lan

ssh nuc "ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 '
uci add dhcp domain
uci set dhcp.@domain[-1].name=\"intranet.nuc.lan\"
uci set dhcp.@domain[-1].ip=\"100.113.153.45\"
uci commit dhcp
/etc/init.d/dnsmasq restart
'"

5.2 Add Traefik route

Edit on NUC: /data/coolify/proxy/dynamic/nuc-services.yaml

Add the following router and service (merge with existing entries):

http:
  routers:
    ecija-intranet:
      rule: Host(`intranet.nuc.lan`)
      service: ecija-intranet
      entryPoints:
        - http
  services:
    ecija-intranet:
      loadBalancer:
        servers:
          - url: http://host.docker.internal:8010

5.3 Update Homepage dashboard

Edit on NUC: /opt/homepage/config/services.yaml

Add under appropriate section:

- ECIJA Intranet:
    - Intranet:
        href: http://intranet.nuc.lan
        description: ECIJA Law Firm Intranet
        icon: django
        server: nuc
        container: ecija-intranet-web

Phase 6: Monitoring & Backup

6.1 Add to Uptime Kuma

Via browser at http://192.168.1.3:3001:

  1. Add new monitor
  2. Type: HTTP(s)
  3. URL: http://192.168.1.3:8010
  4. Name: ECIJA Intranet
  5. Heartbeat interval: 60s

6.2 Add database to Kopia backup

The PostgreSQL volume ecija_pgdata should be included in Kopia's backup schedule.

# Verify the volume exists
ssh nuc "docker volume inspect ecija_pgdata"

# Kopia should auto-discover Docker volumes
# Verify in Kopia UI at http://192.168.1.3:51515

Phase 7: Verification Checklist

Run these commands after deployment to verify everything works:

# 1. Database is running and accessible
ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c 'SELECT count(*) FROM information_schema.tables WHERE table_schema = '\''public'\'';'"
# Expected: ~453 tables

# 2. Extensions are enabled
ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c 'SELECT extname FROM pg_extension;'"
# Expected: pg_trgm, unaccent, plpgsql

# 3. Django container is running
ssh nuc "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | grep ecija"
# Expected: ecija-intranet-web (Up), ecija-intranet-worker (Up)

# 4. Django migrations applied
ssh nuc "docker logs ecija-intranet-web 2>&1 | grep -i 'migrat'"

# 5. Web server responds
ssh nuc "curl -s -o /dev/null -w '%{http_code}' http://localhost:8010/"
# Expected: 200 or 302 (redirect to login)

# 6. GraphQL API responds
ssh nuc "curl -s -o /dev/null -w '%{http_code}' http://localhost:8010/api/graphql/"
# Expected: 200 or 400 (no query provided = normal)

# 7. Background worker is processing
ssh nuc "docker logs ecija-intranet-worker 2>&1 | tail -5"

# 8. DNS resolves
nslookup intranet.nuc.lan
# Expected: 100.113.153.45

# 9. Traefik routes correctly
curl -s -o /dev/null -w '%{http_code}' http://intranet.nuc.lan/
# Expected: 200 or 302

# 10. GCS connectivity (from web container)
ssh nuc "docker exec ecija-intranet-web python -c \"
from google.cloud import storage
client = storage.Client()
bucket = client.bucket('ecija-intranet')
print('GCS connection OK, bucket:', bucket.name)
\""

Quick Reference

Service URLs (after deployment)

Service URL Direct Port
Intranet Web http://intranet.nuc.lan http://192.168.1.3:8010
Intranet DB - 192.168.1.3:5434
DB Admin (Adminer) - http://192.168.1.3:8088
Container Logs - http://192.168.1.3:9999 (Dozzle)

Container Names

Container Purpose
ecija-intranet-db PostgreSQL 17 database
ecija-intranet-web Django + Gunicorn web server
ecija-intranet-worker Background task processor

Database Credentials

Field Value
Host (from container) ecija-intranet-db
Host (from NUC host) 192.168.1.3
Port (internal) 5432
Port (external) 5434
User postgres
Password postgres
Database flexicar-prod

Key File Locations (on NUC)

File Path
Project root /opt/ecija-intranet/
Django app /opt/ecija-intranet/app/
Environment file /opt/ecija-intranet/.env.production
GCP credentials /opt/ecija-intranet/config/credenciales/windy-shoreline-225910-efd33901e56c.json
Dockerfile /opt/ecija-intranet/Dockerfile.nuc
Entrypoint /opt/ecija-intranet/docker-entrypoint.sh
Media volume Docker volume ecija_media
DB volume Docker volume ecija_pgdata
Traefik config /data/coolify/proxy/dynamic/nuc-services.yaml
Homepage config /opt/homepage/config/services.yaml

Key File Locations (on Mac - source files)

File Path
Project source /Users/agutierrez/Desktop/ECIJA-Intranet/
Database dump (directory) /Users/agutierrez/Desktop/flexicar-intranet-backend/dump-flexicar-202602071346-backup/
Database dump (tar) /Users/agutierrez/Desktop/flexicar-intranet-backend/dump-flexicar-202602071346-backup.tar
GCP service account key /Users/agutierrez/Desktop/flexicar-intranet-backend/windy-shoreline-225910-efd33901e56c.json
Installation doc /Users/agutierrez/Desktop/flexicar-intranet-backend/instalacion_intra.docx
Original docker-compose /Users/agutierrez/Desktop/flexicar-intranet-backend/docker-compose.yaml

Coolify Identifiers

Field Value
Server UUID qk84w0goo4w48g4ggsoo0oss
Project UUID a8484ggc88c40w4g4k004ow0
Environment production

Troubleshooting

Django won't start - missing module

# Check logs
ssh nuc "docker logs ecija-intranet-web 2>&1 | tail -30"

# If pip dependency issue, exec into container
ssh nuc "docker exec -it ecija-intranet-web pip install <missing-package>"

Database connection refused

# Verify Postgres is running
ssh nuc "docker ps | grep ecija-intranet-db"

# Check if containers share a network
ssh nuc "docker network inspect ecija-network"

# Test connectivity from web container
ssh nuc "docker exec ecija-intranet-web python -c \"
import psycopg2
conn = psycopg2.connect(host='ecija-intranet-db', port=5432, user='postgres', password='postgres', dbname='flexicar-prod')
print('Connection OK')
conn.close()
\""

Static files not loading (GCS issues)

# Test GCS credentials from container
ssh nuc "docker exec ecija-intranet-web python -c \"
from google.cloud import storage
client = storage.Client()
buckets = list(client.list_buckets())
print('Accessible buckets:', [b.name for b in buckets])
\""

# Verify credentials file is mounted
ssh nuc "docker exec ecija-intranet-web ls -la /secrets/bucket/"

# Run collectstatic manually
ssh nuc "docker exec ecija-intranet-web python manage.py collectstatic --noinput"

pg_restore fails with "role flexicar does not exist"

The dump has OWNER TO flexicar statements. Using --no-owner should handle this, but if you see warnings about role flexicar not existing, you can safely ignore them (objects will be owned by postgres). Alternatively, create the role:

ssh nuc "docker exec ecija-intranet-db psql -U postgres -d flexicar-prod -c 'CREATE ROLE flexicar WITH LOGIN;'" 2>/dev/null || true

Frontend assets not built

# Exec into container and build frontend
ssh nuc "docker exec -it ecija-intranet-web bash -c 'cd /app && npm install && npm run build'"

Memory issues during restore

The dump is 4 GB. If the NUC runs low on memory:

# Use single-job restore (slower but less memory)
ssh nuc "docker exec ecija-intranet-db pg_restore \
  -U postgres \
  -d flexicar-prod \
  --no-owner \
  --no-privileges \
  --jobs=1 \
  /tmp/ecija-dump/"

Container can't reach GCS (network issue)

# Test internet connectivity from container
ssh nuc "docker exec ecija-intranet-web curl -s -o /dev/null -w '%{http_code}' https://storage.googleapis.com/"
# Expected: 200 or 400

# If blocked, check Docker DNS
ssh nuc "docker exec ecija-intranet-web cat /etc/resolv.conf"

Notes

  • The -e git+https://github.com/Seykotron/django-cruds-adminlte.git dependency in requirements.txt requires git to be installed in the Docker image (included in the Dockerfile.nuc)
  • The jinjacompiler npm package in app/package.json uses a GitHub PAT token in its URL - this may expire and need updating
  • Azure AD auth (AG=False) is disabled - the app falls back to Django's built-in auth with username/password
  • GS_CREDENTIALS=/secrets/bucket/credentials.json (generic name) and GS_CREDENTIALS_LOCAL (full filename) both point to the mounted volume at /secrets/bucket/. Both files exist because we copy the JSON as both names in Phase 2.2
  • Background worker (process_tasks) should always be running - it handles async operations like email sending, report generation, etc.
  • The database dump was created from PostgreSQL 17.7 - the container uses PostgreSQL 17 for compatibility
  • DEBUG=True matches the official install doc. Switch to False once everything is confirmed working.
  • The NUC's existing Redis (port 6379) can be used for Django caching if needed - add REDIS_URL to the env file and ensure the containers share a Docker network or use the host IP
  • The database name difference: the dump creates DB flexicar but Django expects flexicar-prod (per install doc). We restore into flexicar-prod using --no-owner which handles this correctly.
  • DATABASE_USER=postgres and DATABASE_PASSWORD=postgres match the official install doc exactly. These are development credentials.