Files
computer-playbook/roles/web-app-pretix/templates/env.j2
kevinveenbirkenbachandClaude Opus 5 eae07f75db fix(web-app-pretix): size gunicorn from the allocated CPUs, not the host
The pretix image derives its worker count as `2 * $(nproc)`, which counts the
host's cores rather than the container's share. On a twenty-core host that is
forty gunicorn workers inside a 2g memory limit and a 1024 pid limit, and the
container is OOM-killed before it serves a request: docker reports exit 137
with OOMKilled true, and the log shows workers taken by SIGKILL.

NUM_WORKERS now applies upstream's own formula to the CPUs the service
declares, with a floor of two so a fractional allocation cannot round to none.

PRETIX_WEB_CONCURRENCY and PRETIX_WORKER_THREADS were already set but the
launcher reads neither for the worker count; they are pretix application
settings, so the role was configuring something that never influenced the
process count.

After the change pretix comes up healthy and stays up across three subsequent
full runs, and its MCP contract probe passes in two of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:52:50 +02:00

55 lines
2.8 KiB
Django/Jinja

{% include 'roles/sys-svc-container/templates/env.j2' %}
PRETIX_PRETIX_INSTANCE_NAME={{ DOMAIN_PRIMARY | upper }} Tickets
PRETIX_PRETIX_ALLOWED_HOSTS={{ PRETIX_HOSTNAME }},127.0.0.1,localhost
PRETIX_PRETIX_URL={{ PRETIX_URL }}
PRETIX_PRETIX_AUTH_BACKENDS=pretix.base.auth.NativeAuthBackend{% if PRETIX_OIDC_ENABLED %},pretix_oidc.auth.OIDCAuthBackend{% endif %}
PRETIX_LOCALE_TIMEZONE={{ HOST_TIMEZONE }}
PRETIX_DATABASE_BACKEND=postgresql
PRETIX_DATABASE_NAME={{ lookup('database', application_id, 'name') }}
PRETIX_DATABASE_USER={{ lookup('database', application_id, 'username') }}
PRETIX_DATABASE_PASSWORD={{ lookup('database', application_id, 'password') | dotenv_quote }}
PRETIX_DATABASE_HOST={{ lookup('database', application_id, 'host') }}
PRETIX_DATABASE_PORT={{ lookup('database', application_id, 'port') }}
PRETIX_WEB_CONCURRENCY={{ POSTGRES_ALLOWED_AVG_CONNECTIONS }}
PRETIX_WORKER_THREADS={{ (POSTGRES_ALLOWED_AVG_CONNECTIONS | int // 2 ) }}
NUM_WORKERS={{ [2, (2 * (lookup('config', application_id, 'services.pretix.cpus') | float)) | round(0, 'floor') | int] | max }}
{% if PRETIX_REDIS_ENABLED %}
{% set redis_host = lookup('engine', 'redis', application_id, 'host') %}
{% set redis_port = lookup('engine', 'redis', application_id, 'port') %}
{% set redis_password = lookup('engine', 'redis', application_id, 'password') %}
{% set redis_auth = ':' ~ redis_password ~ '@' if redis_password else '' %}
PRETIX_REDIS_SESSIONS=true
PRETIX_REDIS_LOCATION=redis://{{ redis_auth }}{{ redis_host }}:{{ redis_port }}/{{ PRETIX_REDIS_CACHE_DB }}
CELERY_BROKER_URL=redis://{{ redis_auth }}{{ redis_host }}:{{ redis_port }}/{{ PRETIX_REDIS_BROKER_DB }}
CELERY_RESULT_BACKEND=redis://{{ redis_auth }}{{ redis_host }}:{{ redis_port }}/{{ PRETIX_REDIS_RESULT_DB }}
{% endif %}
{% set email = lookup('email', application_id) %}
{% if email.enabled | bool %}
PRETIX_MAIL_FROM={{ email['from'] }}
PRETIX_MAIL_HOST={{ email.host }}
PRETIX_MAIL_PORT={{ email.port }}
PRETIX_MAIL_USER={{ email.username }}
PRETIX_MAIL_PASSWORD={{ email.password | dotenv_quote }}
PRETIX_MAIL_TLS={{ 'True' if email.start_tls | bool else 'False' }}
PRETIX_MAIL_SSL={{ 'True' if email.tls | bool else 'False' }}
{% endif %}
{% if PRETIX_OIDC_ENABLED %}
PRETIX_OIDC_TITLE={{ PRETIX_OIDC_LABEL | dotenv_quote }}
PRETIX_OIDC_ISSUER={{ PRETIX_OIDC_ISSUER }}
PRETIX_OIDC_AUTHORIZATION_ENDPOINT={{ PRETIX_OIDC_AUTH_URL }}
PRETIX_OIDC_TOKEN_ENDPOINT={{ PRETIX_OIDC_TOKEN_URL }}
PRETIX_OIDC_USERINFO_ENDPOINT={{ PRETIX_OIDC_USERINFO_URL }}
PRETIX_OIDC_END_SESSION_ENDPOINT={{ PRETIX_OIDC_LOGOUT_URL }}
PRETIX_OIDC_JWKS_URI={{ PRETIX_OIDC_JWKS_URL }}
PRETIX_OIDC_CLIENT_ID={{ PRETIX_OIDC_CLIENT_ID }}
PRETIX_OIDC_CLIENT_SECRET={{ PRETIX_OIDC_CLIENT_SECRET }}
PRETIX_OIDC_SCOPES={{ PRETIX_OIDC_SCOPES }}
PRETIX_OIDC_UNIQUE_ATTRIBUTE={{ PRETIX_OIDC_UNIQUE_ATTRIBUTE }}
{% endif %}