Admin password setup
Every fresh TruePPM install needs one administrator account to sign in with before anyone else can. This page covers getting that first password (during initial setup), finding it again if you missed it, and resetting any user’s password later — including your own, if you lose it.
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 (readable only by its owner) 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@example.com(orDJANGO_SUPERUSER_EMAILif set), usernameadmin(or the local part of the email). The default is an RFC 2606 reserved domain and cannot receive mail, so setDJANGO_SUPERUSER_EMAILbefore the first deploy or password reset for this account will not work.If a non-superuser account already holds that address, it is promoted to superuser and its password is reset. The command prints a warning when it does this. Point
DJANGO_SUPERUSER_EMAILat an unused address if that is not what you want. -
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. 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 by checking each API pod — the file exists on exactly one of them (see the note on replicas below):
for pod in $(kubectl get pods -n <namespace> -o name \ -l app.kubernetes.io/instance=<release>,app.kubernetes.io/component=api); do kubectl exec -n <namespace> "$pod" -c api -- cat /run/trueppm/admin_password 2>/dev/null \ && echo "(from $pod)"doneAt one replica this is the same as kubectl exec deployment/<release>-trueppm-api;
at two or more, deployment/… picks an arbitrary pod and can miss the file.
Get the Deployment name right. The chart’s fullname helper yields
<release>-trueppm-api, not <release>-api — unless the release name already
contains “trueppm”, in which case the duplicate segment is dropped and a release
called trueppm gives plain trueppm-api. When in doubt:
kubectl get deploy -n <namespace> -l app.kubernetes.io/component=apiSet 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”The community edition includes a self-service password reset flow. A user who has forgotten their password clicks Forgot password? on the sign-in page and follows the flow:
/forgot-password— the user enters their work email and requests a reset link. The response is the same whether or not the address has an account, so the page never reveals which emails are registered (no account enumeration).- Reset email — if the address belongs to an account, TruePPM emails a single-use link that is valid for 30 minutes.
/reset-password/confirm— the link opens a page where the user sets a new password (minimum 10 characters, at least one number or symbol, and different from their current password). The link’s single-use credential is carried in the URL fragment (after the#), which browsers never transmit to any server — so it stays out of your reverse-proxy access logs and out of theRefererheader sent to any site the user navigates to next.- All other sessions are signed out — a successful reset revokes every other active session for that account, so a leaked-but-forgotten session on another device cannot outlive the reset.
The reset endpoints are rate limited to blunt abuse (email-bombing a victim and probing for registered addresses).
Requirements and edge cases
Section titled “Requirements and edge cases”- Outbound email must be configured. The reset link can only be delivered once
the
EMAIL_*transport is set (see Configuration). Until then the request still returns success — it never leaks that email is unconfigured — but no message is delivered, so use thechangepasswordfallback below. - SSO accounts. A user who signs in through single sign-on has no local password to reset; the request screen shows a hint that SSO sign-in is unaffected. Their password is managed by their identity provider.
Administrator fallback
Section titled “Administrator fallback”When email is not configured, or a user cannot complete the flow, an administrator
resets a password directly with the same changepassword command used for the admin
account:
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
- Durability & Redundancy — why the
bootstrapinit container behaves this way at two or more replicas - Troubleshooting — what to do when the file is already gone