---
title: "Nginx Proxy Manager — Admin Password Reset via SQLite bcrypt Injection"
aliases: [npm-admin-reset, npm-password-reset, npm-sqlite-reset]
tags: [nginx-proxy-manager, npm, sqlite, homelab, docker, admin]
sources:
  - "daily/2026-04-29.md"
created: 2026-04-29
updated: 2026-04-29
---

# Nginx Proxy Manager — Admin Password Reset via SQLite bcrypt Injection

When locked out of Nginx Proxy Manager (NPM), the admin password can be reset by directly modifying the SQLite database. NPM stores user credentials in a `database.sqlite` file. The process requires stopping NPM first to prevent data corruption, generating a bcrypt hash of the new password, and writing it into the `auth` table.

## Key Points

- **NPM must be stopped before editing the DB** — modifying a live SQLite file risks corruption
- **Always back up the database first** — `cp database.sqlite database.sqlite.bak.YYYY-MM-DD`
- **Password is stored as a bcrypt hash** in the `auth` table
- **Restart NPM after the edit** — NPM reads credentials from the DB at runtime
- **Default admin email** is typically `admin@example.com`; in this homelab it is `admin@ai-impress.com`

## Details

### Step-by-Step Reset Process

```bash
# 1. Stop NPM to prevent corruption
docker compose stop npm

# 2. Navigate to the NPM data directory
cd /opt/npm/data

# 3. Back up the database
cp database.sqlite database.sqlite.bak.$(date +%Y-%m-%d)

# 4. Generate a bcrypt hash for the new password
#    Option A: Python
python3 -c "import bcrypt; print(bcrypt.hashpw(b'admin', bcrypt.gensalt(rounds=10)).decode())"

#    Option B: Node.js (if available)
node -e "const bcrypt = require('bcrypt'); bcrypt.hash('admin', 10).then(h => console.log(h))"

#    Option C: use htpasswd
htpasswd -bnBC 10 "" admin | tr -d ':\n'

# 5. Open the database
sqlite3 database.sqlite

# 6. Find the admin user row
SELECT id, name, email FROM user;
-- or, depending on NPM version:
SELECT id, name, email FROM auth;

# 7. Update the password hash
UPDATE auth SET secret = '$2b$10$YOUR_BCRYPT_HASH_HERE' WHERE identity = 'admin@ai-impress.com';
-- or if the table is named 'user':
UPDATE user SET password = '$2b$10$YOUR_BCRYPT_HASH_HERE' WHERE email = 'admin@ai-impress.com';

# 8. Verify
SELECT identity, secret FROM auth;

# 9. Exit and restart NPM
.quit
docker compose start npm
```

### Table Structure Notes

NPM's SQLite schema varies slightly between versions. In most installations:
- Table `auth` contains columns: `id`, `identity` (email), `secret` (bcrypt hash), `type`
- Table `user` contains: `id`, `name`, `email`, `password`

Run `.tables` and `PRAGMA table_info(auth);` inside sqlite3 to confirm the structure for your version before updating.

### Why NPM Must Be Stopped

SQLite uses file-level locking. NPM holds a write lock on `database.sqlite` while running. Editing the file while NPM is active can corrupt the WAL (write-ahead log) or leave the DB in an inconsistent state. Always stop the container, edit, then restart.

### Homelab Context

This procedure was used when the NPM admin account at `admin@ai-impress.com` became inaccessible. Password was reset to `admin` (minimum viable for internal homelab use). NPM fronts all internal services via reverse proxy and SSL termination at `/opt/npm/`.

## Related Concepts

- [[wiki/concepts/adguard-dns-rewrites-homelab]] — AdGuard DNS rewrites pointing *.ai-impress.com to NPM's IP
- [[wiki/concepts/authentik-homelab-tradeoffs]] — NPM auth_request integration removed when Authentik was retired
- [[wiki/concepts/prowlarr-flaresolverr-get-only]] — similar SQLite direct-edit pattern for Prowlarr cookie injection
- [[wiki/homelab/_index]] — homelab infrastructure; NPM as reverse proxy gateway

## Sources

- [[daily/2026-04-29.md]] — NPM lockout; database.sqlite backup; bcrypt hash injection into auth table; email admin@ai-impress.com; password reset to "admin"; NPM must be stopped during edit
