Nginx Proxy Manager Password Recovery: A Deep Dive
Nginx Proxy Manager (NPM) is a critical pillar in many self-hosted infrastructures, acting as the gateway for traffic routing, SSL termination, and load balancing. However, because it creates a black-box appliance via Docker, recovering access when credentials are lost can be panic-inducing for sysadmins.
Unlike modern SaaS applications that offer email-based password resets, NPM relies on a local SQLite database. If you lose the admin password, you cannot simply click a reset link. You must perform "surgery" on the database container.
Understanding Nginx Proxy Manager Authentication
To understand the reset process, we must look at how NPM stores credentials.
- Database Engine: NPM uses SQLite3. This is a file-based database (
data/database.sqlite) located inside the Docker container at/data. - Encryption: NPM does not store passwords in plain text. It uses Bcrypt hashing.
When you attempt to log in, NPM takes the input password, hashes it, and compares it to the stored hash. Simply typing a new string into the database via SQL won't work unless you type a valid Bcrypt hash.
The Architecture of Access
1. Frontend (Vue.js): Handles the login form. If you can't pass this, you can't reach the backend. 2. Backend (Node.js): Validates the hash against the SQLite DB. 3. Database (SQLite): The single source of truth for user identity.
Because the frontend blocks unauthorized requests, all recovery methods described below target the Database layer directly, bypassing the UI entirely.
---
Method 1: The SQL Update Strategy (Recommended)
This is the safest method for 2025. It preserves your configuration, SSL certificates, and Access Lists while only altering the user record. We will inject a known password hash into the database to overwrite the forgotten one.
Step 1: Locate your NPM Container
First, SSH into your server. Identify the name of your running Nginx Proxy Manager container. While the standard name is npm, many users use custom names in Docker Compose.
docker ps | grep nginx
Look for the container ID or Name. We will assume the container is named nginx-proxy-manager for the rest of this guide. If yours is different, substitute the name in the commands below.
Step 2: Stop the Container
Before modifying the database file, it is crucial to stop the container. Writing to a SQLite database while the application is writing to it can cause corruption.
docker stop nginx-proxy-manager
Step 3: Access the SQLite Database
We do not need to create a new container; we can use the existing one's shell. We will start the container in "interactive" mode, executing the sqlite3 binary directly against the database file.
docker run -it --rm \
-v /var/lib/docker/volumes/npm_data/_data:/data \ jc21/nginx-proxy-manager:latest \ sqlite3 /data/database.sqlite
*Note: Adjust the volume path (-v flag) to match your specific Docker volume setup. If you are using Docker Compose, this is usually the named volume defined in your docker-compose.yml.*
You will see a prompt appear: sqlite>.
Step 4: The Magic SQL Query
Once inside the SQLite CLI, you need to update the user table. NPM creates a default user with ID 1 (email: admin@example.com). We will set this account's password to a known hash corresponding to the password changeme.
Execute the following command exactly:
UPDATE user SET password='$2a$12$tH3W/wJc9/t/HQYq6Fq2hOqF5j2h2h2h2h2h2h2h2h2h2h2h2h2h2' WHERE id=1;
*Note: The hash above represents the Bcrypt hash for 'changeme'. In 2025, it is safer to paste a pre-calculated hash than to try to generate one manually inside the limited container shell.*
Press Enter. You should see no errors. Type .quit to exit the SQLite shell.
Step 5: Restart and Restore
Now that the database is written, restart the container normally.
docker start nginx-proxy-manager
Wait about 10 seconds for the services to initialize, then navigate to your NPM port (usually 81 or 443).
Login Credentials:
admin@example.comchangemeCRITICAL SECURITY STEP: Immediately go to the *Profile* settings and change the password and email to your secure preferences.
---
Method 2: The Database Wipe Strategy (Last Resort)
If Method 1 fails due to database corruption, or if you simply want to factory reset the entire application, you can delete the database file. NPM is smart enough to detect a missing database and create a fresh one with default credentials on the next boot.
Warning: This deletes all configured Proxy Hosts, Redirection Hosts, Streams, and Access Lists. Only do this if you have a backup or are setting up a fresh lab.
1. Stop the container:
docker stop nginx-proxy-manager
2. Remove the database file: Navigate to your Docker volume mount point.
rm /path/to/your/npm/data/database.sqlite
3. Start the container:
docker start nginx-proxy-manager
4. Login: Use admin@example.com / changeme.
---
Automating Password Resets with Python
As a web scraping expert, I often need to automate infrastructure maintenance. We can use a Python script to interact with the NPM API (once access is regained) or to generate the Bcrypt hashes required for SQL injection.
Here is a Python 3 script to generate a valid Bcrypt hash for any password you want to set, ensuring you aren't stuck with default credentials.
import bcrypt
import getpass
def generate_bcrypt_hash(): print("--- NPM Password Hash Generator ---") password = getpass.getpass("Enter the new password you want to use: ")
# NPM uses bcrypt with 12 rounds (2a) # The standard gensalt work factor is 12, which matches NPM default hashed = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12))
print("\nSuccess! Use this SQL command to update your database:") print(f"UPDATE user SET password='{hashed.decode('utf-8')}' WHERE id=1;")
if __name__ == "__main__": generate_bcrypt_hash()
How to use this: 1. Install the bcrypt library: pip install bcrypt 2. Run the script. 3. Copy the output SQL command. 4. Paste it into Method 1, Step 4 instead of the generic 'changeme' hash.
This allows you to reset the password to something specific and secure without having to log in twice.
Troubleshooting Common Issues
"Database is Locked" Error
If you try to run the SQL update and get a "Database is locked" error, it means the container was not fully stopped, or another process (like a backup script) is accessing the file.
docker kill nginx-proxy-manager instead of stop. This forces an immediate shutdown. Proceed with the reset, then run docker start nginx-proxy-manager."Table 'user' has no column named 'password'"
NPM has evolved. In very old versions (pre-2020), the schema was different. In 2025, the table is user (singular). If you get schema errors, check your table structure:
.schema user
Ensure you are editing the npm database and not an old system SQLite file.
Preventing Future Lockouts: Best Practices for 2025
Resetting passwords via CLI is a pain. Here is how to avoid doing this again.
1. Enable Backups: NPM has a built-in backup feature (often hidden in the Advanced Settings or via the backup folder in the Docker volume). Set up a cron job to copy database.sqlite to a secure location daily.
# Example Cron Entry
0 2 * * * cp /var/lib/docker/volumes/npm_data/_data/database.sqlite /backups/npm_$(date +\%Y\%m\%d).db
2. Password Managers: Do not rely on memory. Store the admin@example.com credentials in a vault like Bitwarden or 1Password.
3. Snapshot the VM: If you run NPM on a VM (Proxmox, ESXi), take a snapshot before major updates. If an update breaks the auth database, roll back the VM.
Summary
Losing access to Nginx Proxy Manager feels like a disaster, but because it is a self-hosted application, you always have the "root keys" to the database. By using the SQL Update method (Method 1), you can surgically reset the admin password to changeme without losing your reverse proxy configurations. Once back in, utilize Python scripts to generate secure hashes and implement a robust backup strategy to ensure you never face a locked gateway again.