4.7 KiB
Nginx Proxy Manager migration
RustProxy ships scripts/migrate_npm.py for a one-time, dry-run-first migration of
Nginx Proxy Manager (NPM) proxy hosts from its SQLite database.
The tool reads NPM only. It does not modify the NPM database and it never imports
Let's Encrypt/private-key material. It logs in to the RustProxy administration API,
reads the current revision, merges generated vhosts into the existing configuration,
and writes only when --apply is supplied.
migrate_npm.py is the CLI entry point and requires the sibling modules
scripts/migrate_npm_core.py and scripts/migrate_npm_api.py. They use only the
Python standard library, so no pip packages are required. If the migrator is copied
or mounted outside the RustProxy source tree, keep all three files in the same directory.
What is migrated
For each NPM proxy_host row:
domain_names-> RustProxy vhost hostnames, including leading*.wildcards;forward_scheme,forward_host,forward_port-> one RustProxy backend;allow_websocket_upgrade-> vhost WebSocket policy;enabled-> vhost enabled state;http2_support-> vhost HTTP/2 policy;- NPM hosts with a certificate/forced SSL -> RustProxy TLS listener. Certificate
files are not copied: the application-managed RustProxy default certificate is
used unless
--certificate-idnames an existing RustProxy inventory certificate; ssl_forced-> RustProxyforce_ssl=true. When HTTP and HTTPS listeners exist, both are attached so cleartext requests can receive RustProxy's308redirect to HTTPS. If no TLS listener exists, an SSL-forced NPM host is skipped instead of being imported with weaker semantics.
Migrated backends use allow_private=true because NPM commonly proxies to LAN or
container origins. The migration tool follows RustProxy 0.22.1 and imports backend tls_verify=false unless you later enable verification explicitly.
Use --npm-compatible-backend-tls when an NPM HTTPS origin depends on
the common Nginx behavior of not verifying the upstream certificate; review that
exception after migration.
The following are reported for manual review rather than guessed: NPM access lists,
advanced Nginx snippets, custom locations, NPM cache settings and block_exploits.
Existing RustProxy hostname ownership is never silently replaced; conflicting NPM
rows are skipped and shown in the report.
SQLite location
The standard NPM SQLite path inside the container is /data/database.sqlite. On a
Docker host with a bind mount, pass the corresponding host path. For a named volume,
docker volume inspect shows its mountpoint.
Read the live mounted database rather than copying only database.sqlite from a
running WAL-mode instance; copying without its WAL file can omit recent changes.
Dry run
Set the password in the environment or omit it and use the interactive prompt:
export RUSTPROXY_USERNAME=admin
export RUSTPROXY_PASSWORD='...'
python3 scripts/migrate_npm.py \
--database /path/to/npm/data/database.sqlite \
--rustproxy https://rustproxy.example.net:9090
The default run is read-only. It creates npm-migration-plan.json with mode 0600
and prints every imported/skipped NPM row plus manual-review warnings.
If the administration panel intentionally uses the application self-signed certificate and the script connects directly to it, add:
--insecure-admin-tls
Prefer --ca-file /path/to/ca.pem or a hostname with a trusted certificate where
available.
By default the first enabled HTTP listener and first enabled TLS listener are used.
For ssl_forced hosts both are selected when available. --force-https applies the
same Force SSL policy to every imported host. Select explicit listener IDs when needed:
--http-listener public-http --https-listener public-https
Apply
After reviewing npm-migration-plan.json:
python3 scripts/migrate_npm.py \
--database /path/to/npm/data/database.sqlite \
--rustproxy https://rustproxy.example.net:9090 \
--apply
The write uses the revision returned by GET /api/config; a concurrent RustProxy
configuration change causes the API compare-and-swap to fail rather than overwrite a
newer revision.
Running from a helper container
If the Docker host does not have Python 3, mount the NPM data read-only and mount the RustProxy source/script into a temporary Python container. For example, from the RustProxy directory:
docker run --rm -it \
--network host \
-v /path/to/npm/data:/npm:ro \
-v "$PWD/scripts:/rustproxy-scripts:ro" \
python:3.13-alpine \
python /rustproxy-scripts/migrate_npm.py \
--database /npm/database.sqlite \
--rustproxy https://127.0.0.1:9090 \
--insecure-admin-tls
Use --apply only after the dry-run plan is reviewed.