Admin password setup
TruePPM ships a create_admin Django management command that bootstraps a superuser on first run. The default writes a securely-generated password to a file with 0o600 permissions so the credential never appears in container logs or log aggregators (CloudWatch, Datadog, etc.).
First-run setup
Section titled “First-run setup”The api container runs create_admin automatically on startup (both in docker compose and in the Helm chart). On first run it:
- Checks whether any superuser already exists. If yes, it exits silently — re-deploys never overwrite a production password.
- Generates a URL-safe random password (16 bytes of entropy, about 22 characters), or honors
DJANGO_SUPERUSER_PASSWORDif set. - Creates the superuser with email
admin@trueppm.dev(orDJANGO_SUPERUSER_EMAILif set), usernameadmin(or the local part of the email). - Writes the password to
/tmp/trueppm_admin_passwordwith mode0o600.
Retrieve the first-run password
Section titled “Retrieve the first-run password”docker compose
Section titled “docker compose”docker compose exec api cat /tmp/trueppm_admin_passwordThen delete the file — the command writes it once for retrieval, but a long-lived file on a shared /tmp is bad operational hygiene.
docker compose exec api rm /tmp/trueppm_admin_passwordKubernetes / Helm
Section titled “Kubernetes / Helm”The chart writes the one-time password to /run/trueppm/admin_password, an emptyDir mount the chart provides (lost when the pod restarts — fine for first-run-only retrieval). The path is controlled by the admin.passwordFile value, which the chart renders into the TRUEPPM_ADMIN_PASSWORD_FILE env var:
admin: passwordFile: /run/trueppm/admin_password # chart defaultRetrieve it with:
kubectl exec deployment/<release>-api -- cat /run/trueppm/admin_passwordSet a known password at startup
Section titled “Set a known password at startup”Pass DJANGO_SUPERUSER_PASSWORD to the api container:
services: api: environment: DJANGO_SUPERUSER_EMAIL: admin@example.com DJANGO_SUPERUSER_USERNAME: admin DJANGO_SUPERUSER_PASSWORD: <your password>This is convenient for local development but do not use this pattern in production — env vars in compose files are versioned and visible in process listings.
Rotate the password (after first run)
Section titled “Rotate the password (after first run)”The create_admin command is intentionally a no-op when a superuser already exists, so you cannot use it to rotate. Use Django’s standard changepassword command instead:
docker compose
Section titled “docker compose”docker compose exec api python manage.py changepassword adminYou’ll be prompted for the new password twice, interactively.
Kubernetes
Section titled “Kubernetes”kubectl exec -it <api-pod> -- python manage.py changepassword adminProgrammatic rotation
Section titled “Programmatic rotation”If you need to rotate non-interactively (e.g. from a CI job or rotation script):
docker compose exec -T api python manage.py shell <<'EOF'from django.contrib.auth import get_user_modelUser = get_user_model()admin = User.objects.get(username='admin')admin.set_password('<new password>')admin.save()EOFPass the new password via stdin/env from a secret manager — never inline.
End-user password reset
Section titled “End-user password reset”Self-service password reset is not yet available in the community edition —
there is no public reset endpoint or reset-email flow today. Until it ships, an
administrator resets a user’s password directly with the same changepassword
command used for the admin account below:
docker compose exec api python manage.py changepassword <username>Forgot the admin password (no email configured)
Section titled “Forgot the admin password (no email configured)”If you have lost the admin password and SMTP is not configured (common in self-hosted dev), shell into the container and reset directly:
docker compose exec api python manage.py changepassword adminIf you cannot recall the username, list superusers:
docker compose exec -T api python manage.py shell <<'EOF'from django.contrib.auth import get_user_modelfor u in get_user_model().objects.filter(is_superuser=True): print(u.username, u.email)EOFSecurity notes
Section titled “Security notes”- The default password file path (
/tmp/trueppm_admin_password) usesO_NOFOLLOWto defeat symlink attacks on the world-writable/tmpdirectory (Linux and macOS). - The file is created with mode
0o600atomically viaos.open(..., 0o600)— there is no TOCTOU window between create and chmod. - If the file write fails, the password falls back to management-command stdout only — it is never sent through
logger.warning()or higher because log aggregators forward those lines downstream. - For production deployments, override
TRUEPPM_ADMIN_PASSWORD_FILEto a non-world-writable location (anemptyDirvolume, aSecretmount, or a host-bind to a 0700 directory).
Related
Section titled “Related”- Installation — how the api container is started
- Configuration — environment variables reference
- Security — broader hardening guide