PiVPN Web maintains its own user store in a SQLite database managed by TypeORM. Passwords are hashed with bcrypt (cost factor 10) before they are written to the database, so plaintext credentials are never persisted. User accounts are seeded automatically the first time the application starts, using the credentials you supply via environment variables. No manual database setup is required.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/AZhur771/pivpn-web/llms.txt
Use this file to discover all available pages before exploring further.
User roles
PiVPN Web supports three distinct roles. Each role is controlled by a pair of environment variables — a username and a password.Admin
The admin account has full access to the dashboard. An admin can:- List, create, and delete WireGuard clients
- Enable and disable individual clients
- View QR codes and download client configuration files
ADMIN_USER and ADMIN_PASSWORD. The admin account is stored with admin: true in the database.
Viewer
The viewer account has read-only access. A viewer can browse the client list and inspect connection status but cannot make any changes. This is useful for giving monitoring access to a team member without granting administrative privileges. Set viaVIEWER_USER and VIEWER_PASSWORD. The viewer account is stored with admin: false in the database.
Tech
The tech account has admin-level access (stored withadmin: true) and is intended for automation, scripts, or integrations that need programmatic access to the API. It is seeded by a separate migration so that it can be omitted independently of the viewer account.
Set via TECH_USER and TECH_PASSWORD.
Environment variable reference
| Role | Username variable | Password variable | admin flag |
|---|---|---|---|
| Admin | ADMIN_USER | ADMIN_PASSWORD | true |
| Viewer | VIEWER_USER | VIEWER_PASSWORD | false |
| Tech | TECH_USER | TECH_PASSWORD | true |
If
VIEWER_USER / VIEWER_PASSWORD or TECH_USER / TECH_PASSWORD are not set when the container first starts, those accounts are not created. The migrations insert rows only for the variables that are present at migration time.User entity structure
Each account is stored as a row in theuser table with the following columns, defined in lib/entities/User.ts:
How accounts are seeded
Accounts are created by TypeORM migrations that run once on first startup:AddUsersmigration — inserts the admin and viewer accounts usingADMIN_USER,ADMIN_PASSWORD,VIEWER_USER, andVIEWER_PASSWORD.AddTechUsersmigration — inserts the tech account usingTECH_USERandTECH_PASSWORD.
bcrypt.genSalt(10) before inserting the row, so the plaintext value from the environment variable is never stored.
INSERT statements execute exactly once. Restarting the container does not re-seed the database.