Migrate from SQLite to PostgreSQL
This guide is part of the Splitting Your Self-Hosted Deployment guide. It covers migrating your Management server database from SQLite to PostgreSQL.
The default NetBird deployment uses SQLite, which stores all data in a single file. This works well for smaller setups, but you may want to migrate to PostgreSQL if:
- You want the database on a separate, dedicated machine
- You need better concurrency handling for larger deployments
- You prefer the operational tooling and backup options that PostgreSQL provides
For smaller teams, SQLite is perfectly capable and migration is not required.
Set Up PostgreSQL
If you don't already have a PostgreSQL instance, you can run one in Docker:
docker run -d \
--name postgres-server \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
postgres:16
Replace the default password with a strong, unique password for production deployments.
Back Up the SQLite Store
Before migrating, create a backup of your SQLite database:
mkdir backup
docker compose cp -a netbird-server:/var/lib/netbird/. backup/
Install pgloader
The migration uses pgloader to transfer data from SQLite to PostgreSQL:
# Debian/Ubuntu
sudo apt-get install pgloader
# macOS
brew install pgloader
Create the Migration File
Create a file called sqlite.load with the following content:
LOAD DATABASE
FROM sqlite:///root/combined/backup/store.db
INTO postgresql://postgres:password@localhost:5432/postgres
WITH include drop, create tables, create indexes, reset sequences
CAST
column accounts.is_domain_primary_account to boolean,
column accounts.settings_peer_login_expiration_enabled to boolean,
column accounts.settings_peer_inactivity_expiration_enabled to boolean,
column accounts.settings_regular_users_view_blocked to boolean,
column accounts.settings_groups_propagation_enabled to boolean,
column accounts.settings_jwt_groups_enabled to boolean,
column accounts.settings_routing_peer_dns_resolution_enabled to boolean,
column accounts.settings_extra_peer_approval_enabled to boolean,
column accounts.settings_extra_user_approval_required to boolean,
column accounts.settings_lazy_connection_enabled to boolean
;
Update the SQLite path and PostgreSQL connection string to match your environment.
Run the Migration
pgloader sqlite.load
Update config.yaml
On your main server, update the store section in config.yaml to use PostgreSQL:
server:
# ... existing settings ...
store:
engine: "postgres"
dsn: "host=postgres-server user=postgres password=password dbname=postgres port=5432"
Alternatively, you can pass the connection string as an environment variable instead of putting it in the config file:
netbird-server:
environment:
- NETBIRD_STORE_ENGINE_POSTGRES_DSN=host=postgres-server user=postgres password=password dbname=postgres port=5432
Restart and Verify
docker compose up -d
Check the logs to confirm PostgreSQL is being used:
docker compose logs netbird-server | grep store
You should see:
using Postgres store engine
Migrate Auth Store
The embedded identity provider (Dex) uses a separate SQLite database (idp.db) to store authentication data such as users, passwords, connectors, sessions, and tokens. You can optionally migrate this to PostgreSQL as well.
Create the Auth Database
docker exec postgres-server psql -U postgres -c "CREATE DATABASE netbird_auth;"
Create the Auth Migration File
Create a file called auth-sqlite.load with the following content:
LOAD DATABASE
FROM sqlite:///root/combined/backup/idp.db
INTO postgresql://postgres:password@localhost:5432/netbird_auth
WITH include drop, create tables, create indexes, reset sequences
CAST
column client.public to boolean,
column auth_request.force_approval_prompt to boolean,
column auth_request.logged_in to boolean,
column auth_request.claims_email_verified to boolean,
column auth_code.claims_email_verified to boolean,
column refresh_token.claims_email_verified to boolean
;
Update the SQLite path and PostgreSQL connection string to match your environment.
Run the Auth Migration
pgloader auth-sqlite.load
Update config.yaml
Add the authStore section to your config.yaml:
server:
# ... existing settings ...
authStore:
engine: "postgres"
dsn: "host=postgres-server port=5432 user=postgres password=password dbname=netbird_auth sslmode=disable"
Restart and Verify
docker compose up -d
Migrate Activity Store
The activity events store uses a separate SQLite database (events.db) to record user and system activity. You can optionally migrate this to PostgreSQL as well.
Create the Activity Database
docker exec postgres-server psql -U postgres -c "CREATE DATABASE netbird_events;"
Create the Activity Migration File
Create a file called activity-sqlite.load with the following content:
LOAD DATABASE
FROM sqlite:///root/combined/backup/events.db
INTO postgresql://postgres:password@localhost:5432/netbird_events
WITH include drop, create tables, create indexes, reset sequences
;
No boolean CAST rules are needed for the activity store — all columns use standard integer and text types.
Update the SQLite path and PostgreSQL connection string to match your environment.
Run the Activity Migration
pgloader activity-sqlite.load
Update config.yaml
Add the activityStore section to your config.yaml:
server:
# ... existing settings ...
activityStore:
engine: "postgres"
dsn: "host=postgres-server port=5432 user=postgres password=password dbname=netbird_events sslmode=disable"
Restart and Verify
docker compose up -d

