First Run and Bootstrap
What happens when the Stake Portal starts for the first time and how to complete the initial setup.
The first time the backend starts against an empty database, it automatically bootstraps everything needed to make the portal usable. No manual SQL or seed scripts are required.
This page assumes you have already completed the Docker Deployment guide and your containers are running.
Before You Start
Two things must be in place before the first boot:
1. INITIAL_ADMIN_PASSWORD must be set in backend/.env
INITIAL_ADMIN_PASSWORD=choose-a-strong-temporary-passwordIf this variable is unset and the database is empty, the backend exits immediately at startup with a ValueError. After the first successful boot (once at least one user exists in the database), this variable is ignored on all subsequent starts.
2. backend/wards.csv must exist
The docker-compose.yml bind-mounts ./backend/wards.csv into the container at /app/wards.csv. If the file is missing when the container starts, the backend boots but loads no wards, and /api/wards returns empty.
Create the file before starting the stack:
name,start_hour
Riverside Ward,9.0
Eastside Ward,11.0
Westview Ward,13.5
Singles Ward,9.0name— the display name of the wardstart_hour— meeting start time as a decimal hour (9.0 = 9:00 AM, 13.5 = 1:30 PM)
Add a row for every ward in your stake. See Configuring Wards below for more details.
What Happens on First Boot
When the backend detects an empty database (zero users), it runs the following bootstrap steps in order:
Admin account created — [email protected] is created with the password from INITIAL_ADMIN_PASSWORD and the force_password_reset flag set to true.
System callings created — Three callings are inserted: High Councilor, Stake Presidency, and Bishop. These are system-defined and cannot be deleted through the UI.
Wards loaded — Rows are created for every ward defined in wards.csv. The file is read from the path in WARD_DEFINITION_FILE (default: /app/wards.csv in Docker).
The following steps run on every startup, not just the first. They are idempotent — they only create rows if they do not already exist:
- Temple recommend config seeded (singleton row with default values)
- Site settings seeded (singleton row: stake name, hero text, contact info)
- Default appointment types created (Temple Recommend, Limited Use Recommend, etc.)
- Fast Sunday exceptions pre-populated for upcoming Fast Sundays
On every startup the backend also re-reads wards.csv and creates any new rows that do not yet exist in the database. Existing ward rows are never overwritten, so editing ward names directly in the database after first boot is safe.
Configuring Wards
The ward roster is driven by backend/wards.csv. Edit this file before the first boot to match your stake's ward structure.
name,start_hour
Riverside Ward,9.0
Eastside Ward,11.0
Westview Ward,13.5
Singles Ward,9.0Adding new rows at any time is safe — they are picked up on the next backend restart. The backend does not modify or delete existing Ward rows based on changes to the CSV. If you need to rename or remove a ward after first boot, edit the database directly (via SQLite browser or the admin UI when available).
In Docker, verify the bind mount is working after the container starts:
docker compose exec backend cat /app/wards.csvIf this returns an error or empty output, the file was missing or the bind mount was not applied. Create or fix the file and restart:
docker compose restart backendVerifying Bootstrap in the Logs
After docker compose up -d, tail the backend logs to confirm bootstrap completed:
docker compose logs -f backendLook for lines like:
INFO: Application startup complete.If you see a ValueError instead, the most common causes are:
JWT_SECRET_KEYis missing, under 64 characters, or a known weak placeholderINITIAL_ADMIN_PASSWORDis unset and the database is empty
First Login
Once the backend is healthy, open the portal in your browser at your configured domain:
https://yourstake.org/loginOr locally during development:
http://localhost:3100/loginLog in with:
- Email:
[email protected] - Password: the value you set for
INITIAL_ADMIN_PASSWORD
Forced Password Change
The admin account is created with force_password_reset=true. After login, the portal immediately redirects to /change-password. You must set a new password before accessing any other page.
This forced-reset behaviour applies to any user account with force_password_reset enabled — not just the admin. When creating accounts for stake leaders, administrators can enable this flag so users choose their own password on first login.
After setting a new password, you are taken to the home page with full access to the Leader Portal.
Confirming Wards Loaded
After logging in, verify that your wards loaded correctly:
- Navigate to
/ward-info/meeting-times— you should see your ward list with meeting times - Or visit
/api/wardsdirectly (while authenticated) — returns a JSON array of ward objects
If the list is empty, check that backend/wards.csv is correctly formatted and restart the backend: docker compose restart backend.
Next Steps
- Visit
/leader/user-adminto create accounts for stake leaders - Confirm wards loaded at
/ward-info/meeting-times - Review calling permissions for each user
- Visit the site settings page to configure the stake name, logo, and contact details
- Remove or blank out
INITIAL_ADMIN_PASSWORDinbackend/.env— it is no longer needed