This commit is contained in:
Matt Grutza 2026-01-21 23:59:28 +00:00 committed by GitHub
commit f9781394e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 164 additions and 24 deletions

View file

@ -30,6 +30,12 @@ WORKDIR /app
COPY . /app
# Copy nginx configuration
COPY ./docker/nginx.conf /etc/nginx/sites-enabled/default
# Fix line endings and make entrypoint scripts executable
RUN for f in /app/docker/entrypoint*.sh; do \
if [ -f "$f" ]; then \
sed -i 's/\r$//' "$f" && chmod +x "$f"; \
fi; \
done
# Clean out existing frontend folder
RUN rm -rf /app/frontend
# Copy built frontend assets

View file

@ -10,6 +10,7 @@ services:
- db
- redis
environment:
- DISPATCHARR_ENV=modular
- POSTGRES_HOST=db
- POSTGRES_DB=dispatcharr
- POSTGRES_USER=dispatch
@ -25,7 +26,6 @@ services:
# Lower values = higher priority. Range: -20 (highest) to 19 (lowest)
# Negative values require cap_add: SYS_NICE (uncomment below)
#- UWSGI_NICE_LEVEL=-5 # uWSGI/FFmpeg/Streaming (default: 0, recommended: -5 for high priority)
#- CELERY_NICE_LEVEL=5 # Celery/EPG/Background tasks (default: 5, low priority)
#
# Uncomment to enable high priority for streaming (required if UWSGI_NICE_LEVEL < 0)
#cap_add:
@ -52,22 +52,27 @@ services:
depends_on:
- db
- redis
- web
volumes:
- ../:/app
- ./data:/data
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- DISPATCHARR_ENV=modular
- POSTGRES_HOST=db
- POSTGRES_DB=dispatcharr
- POSTGRES_USER=dispatch
- POSTGRES_PASSWORD=secret
- REDIS_HOST=redis
- CELERY_BROKER_URL=redis://redis:6379/0
command: >
bash -c "
cd /app &&
nice -n 5 celery -A dispatcharr worker -l info
"
- DISPATCHARR_LOG_LEVEL=info
#- CELERY_NICE_LEVEL=5 #Celery/EPG/Background tasks (default:5, low priority; Range: -20 to 19)
- DJANGO_SETTINGS_MODULE=dispatcharr.settings
- PYTHONUNBUFFERED=1
# Uncomment to enable high priority for Celery (required if CELERY_NICE_LEVEL < 0)
#cap_add:
# - SYS_NICE
entrypoint: ["/app/docker/entrypoint.celery.sh"]
db:
image: postgres:14
@ -80,10 +85,20 @@ services:
- POSTGRES_PASSWORD=secret
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dispatch -d dispatcharr"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:latest
container_name: dispatcharr_redis
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:

View file

@ -0,0 +1,29 @@
#!/bin/bash
set -e
cd /app
source /dispatcharrpy/bin/activate
# Wait for Django secret key
echo 'Waiting for Django secret key...'
while [ ! -f /data/jwt ]; do sleep 1; done
export DJANGO_SECRET_KEY="$(tr -d '\r\n' < /data/jwt)"
# Wait for migrations to complete (check that NO unapplied migrations remain)
echo 'Waiting for migrations to complete...'
until ! python manage.py showmigrations 2>&1 | grep -q '\[ \]'; do
echo 'Migrations not ready yet, waiting...'
sleep 2
done
# Start Celery
echo 'Migrations complete, starting Celery...'
celery -A dispatcharr beat -l info &
# Default to nice level 5 (lower priority) - safe for unprivileged containers
# Negative values require SYS_NICE capability
NICE_LEVEL="${CELERY_NICE_LEVEL:-5}"
if [ "$NICE_LEVEL" -lt 0 ] 2>/dev/null; then
echo "Warning: CELERY_NICE_LEVEL=$NICE_LEVEL is negative, requires SYS_NICE capability"
fi
nice -n "$NICE_LEVEL" celery -A dispatcharr worker -l info --autoscale=6,1

View file

@ -66,7 +66,7 @@ PY
mv -f "$tmpfile" "$SECRET_FILE" || { echo "move failed"; rm -f "$tmpfile"; exit 1; }
umask $old_umask
fi
export DJANGO_SECRET_KEY="$(cat "$SECRET_FILE")"
export DJANGO_SECRET_KEY="$(tr -d '\r\n' < "$SECRET_FILE")"
# Process priority configuration
# UWSGI_NICE_LEVEL: Absolute nice value for uWSGI/streaming (default: 0 = normal priority)
@ -150,26 +150,54 @@ fi
# Run init scripts
echo "Starting user setup..."
. /app/docker/init/01-user-setup.sh
echo "Setting up PostgreSQL..."
. /app/docker/init/02-postgres.sh
# Initialize PostgreSQL if NOT in modular mode (using external database)
if [[ "$DISPATCHARR_ENV" != "modular" ]]; then
echo "Setting up PostgreSQL..."
. /app/docker/init/02-postgres.sh
fi
echo "Starting init process..."
. /app/docker/init/03-init-dispatcharr.sh
# Start PostgreSQL
echo "Starting Postgres..."
su - postgres -c "$PG_BINDIR/pg_ctl -D ${POSTGRES_DIR} start -w -t 300 -o '-c port=${POSTGRES_PORT}'"
# Wait for PostgreSQL to be ready
until su - postgres -c "$PG_BINDIR/pg_isready -h ${POSTGRES_HOST} -p ${POSTGRES_PORT}" >/dev/null 2>&1; do
echo_with_timestamp "Waiting for PostgreSQL to be ready..."
sleep 1
done
postgres_pid=$(su - postgres -c "$PG_BINDIR/pg_ctl -D ${POSTGRES_DIR} status" | sed -n 's/.*PID: \([0-9]\+\).*/\1/p')
echo "✅ Postgres started with PID $postgres_pid"
pids+=("$postgres_pid")
# Start PostgreSQL if NOT in modular mode (using external database)
if [[ "$DISPATCHARR_ENV" != "modular" ]]; then
echo "Starting Postgres..."
su - postgres -c "$PG_BINDIR/pg_ctl -D ${POSTGRES_DIR} start -w -t 300 -o '-c port=${POSTGRES_PORT}'"
# Wait for PostgreSQL to be ready
until su - postgres -c "$PG_BINDIR/pg_isready -h ${POSTGRES_HOST} -p ${POSTGRES_PORT}" >/dev/null 2>&1; do
echo_with_timestamp "Waiting for PostgreSQL to be ready..."
sleep 1
done
postgres_pid=$(su - postgres -c "$PG_BINDIR/pg_ctl -D ${POSTGRES_DIR} status" | sed -n 's/.*PID: \([0-9]\+\).*/\1/p')
echo "✅ Postgres started with PID $postgres_pid"
pids+=("$postgres_pid")
else
echo "🔗 Modular mode: Using external PostgreSQL at ${POSTGRES_HOST}:${POSTGRES_PORT}"
# Wait for external PostgreSQL to be ready using Python (no pg_isready needed)
echo_with_timestamp "Waiting for external PostgreSQL to be ready..."
until python3 -c "
import socket
import sys
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
s.connect(('${POSTGRES_HOST}', ${POSTGRES_PORT}))
s.close()
sys.exit(0)
except Exception:
sys.exit(1)
" 2>/dev/null; do
echo_with_timestamp "Waiting for PostgreSQL at ${POSTGRES_HOST}:${POSTGRES_PORT}..."
sleep 1
done
echo "✅ External PostgreSQL is ready"
fi
# Ensure database encoding is UTF8
. /app/docker/init/02-postgres.sh
ensure_utf8_encoding
# Ensure database encoding is UTF8 (only for internal database)
if [[ "$DISPATCHARR_ENV" != "modular" ]]; then
ensure_utf8_encoding
fi
if [[ "$DISPATCHARR_ENV" = "dev" ]]; then
. /app/docker/init/99-init-dev.sh
@ -197,6 +225,9 @@ if [ "$DISPATCHARR_ENV" = "dev" ] && [ "$DISPATCHARR_DEBUG" != "true" ]; then
elif [ "$DISPATCHARR_DEBUG" = "true" ]; then
echo "🚀 Starting uwsgi in debug mode..."
uwsgi_file="/app/docker/uwsgi.debug.ini"
elif [ "$DISPATCHARR_ENV" = "modular" ]; then
echo "🚀 Starting uwsgi in modular mode..."
uwsgi_file="/app/docker/uwsgi.modular.ini"
else
echo "🚀 Starting uwsgi in production mode..."
uwsgi_file="/app/docker/uwsgi.ini"

59
docker/uwsgi.modular.ini Normal file
View file

@ -0,0 +1,59 @@
[uwsgi]
; Modular deployment mode - external PostgreSQL, Redis, and Celery
; Remove file creation commands since we're not logging to files anymore
; exec-pre = mkdir -p /data/logs
; exec-pre = touch /data/logs/uwsgi.log
; exec-pre = chmod 666 /data/logs/uwsgi.log
; First run Redis availability check script once
exec-pre = python /app/scripts/wait_for_redis.py
; Start Daphne for WebSocket support (required for real-time features)
; Redis and Celery run in separate containers in modular mode
attach-daemon = daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application
# Core settings
chdir = /app
module = dispatcharr.wsgi:application
virtualenv = /dispatcharrpy
master = true
env = DJANGO_SETTINGS_MODULE=dispatcharr.settings
env = USE_NGINX_ACCEL=true
socket = /app/uwsgi.sock
chmod-socket = 777
vacuum = true
die-on-term = true
static-map = /static=/app/static
# Worker management
workers = 4
# Optimize for streaming
http = 0.0.0.0:5656
http-keepalive = 1
buffer-size = 65536 # Increase buffer for large payloads
post-buffering = 4096 # Reduce buffering for real-time streaming
http-timeout = 600 # Prevent disconnects from long streams
socket-timeout = 600 # Prevent write timeouts when client buffers
lazy-apps = true # Improve memory efficiency
# Async mode (use gevent for high concurrency)
gevent = 400 # Each unused greenlet costs ~2-4KB of memory
# Higher values have minimal performance impact when idle, but provide capacity for traffic spikes
# If memory usage becomes an issue, reduce this value
# Performance tuning
thunder-lock = true
log-4xx = true
log-5xx = true
disable-logging = false
# Logging configuration
# Enable console logging (stdout)
log-master = true
# Enable strftime formatting for timestamps
logformat-strftime = true
log-date = %%Y-%%m-%%d %%H:%%M:%%S,000
# Use formatted time with environment variable for log level
log-format = %(ftime) $(DISPATCHARR_LOG_LEVEL) uwsgi.requests Worker ID: %(wid) %(method) %(status) %(uri) %(msecs)ms
log-buffering = 1024 # Add buffer size limit for logging