Shubham
07/08/2026, 8:06 PMMarvin
07/08/2026, 8:06 PMMarvin
07/08/2026, 8:08 PMprefect version shows:
- Server:
- Database: sqlite
- prefect config view does not show any database connection setting at all
- In your systemd unit, the PostgreSQL URL is set as:
- PREFECT_API_DATABASE_CONNECTION_URL=...
- But your current shell session where you ran prefect version / prefect config view does not appear to have that env var loaded
A couple important points:
1. PREFECT_API_DATABASE_CONNECTION_URL is the correct setting
Prefect 3 supports that variable for the server DB connection.
Your value should look like:
bash
<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>
2. Your systemd file has a mistake
You currently have angle brackets around the connection string:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
Those < > should not be there.
Use:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
Also, since your password contains @, URL-encoding it as %40 is correct.
3. How to verify what the running systemd service is using
Run these:
bash
systemctl daemon-reload
systemctl restart prefect-server
systemctl show prefect-server -p Environment
journalctl -u prefect-server -n 100 --no-pager
That will show whether the service actually has the DB env var.
Also check from the same shell as the service config expectation:
bash
prefect config view --show-secrets | grep DATABASE
prefect version
If Prefect is using PostgreSQL, prefect version should show something like:
text
Server:
Database: postgresql
PostgreSQL version: ...
4. Recommended corrected service file
ini
[Unit]
Description=Prefect Server
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/home/prefect
Environment="PREFECT_HOME=/home/prefect/.prefect"
Environment="PREFECT_SERVER_API_HOST=192.168.29.151"
Environment="PREFECT_SERVER_API_PORT=4200"
Environment="PREFECT_SERVER_API_KEEPALIVE_TIMEOUT=60"
Environment="PREFECT_SERVER_LOGGING_LEVEL=DEBUG"
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
Environment="PREFECT_SERVER_API_AUTH_STRING=astra:Network@5714"
ExecStart=/home/prefect/.venv/bin/prefect server start --host 0.0.0.0 --port 4200 --keep-alive-timeout 60
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Then:
bash
systemctl daemon-reload
systemctl restart prefect-server
journalctl -u prefect-server -f
5. Best quick test
Temporarily export the DB URL in your current shell and run:
bash
export PREFECT_API_DATABASE_CONNECTION_URL="<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
prefect config view --show-secrets | grep DATABASE
prefect version
If that switches prefect version from sqlite to postgresql, then the issue is just your systemd environment/config.
Docs / references
- Prefect settings and configuration: Prefect settings
- Prefect self-hosted server docs: Prefect Server
If you want, I can also help you with a step-by-step PostgreSQL migration checklist for this exact server.Shubham
07/08/2026, 8:12 PMMarvin
07/08/2026, 8:12 PMMarvin
07/08/2026, 8:12 PM< and > characters in the systemd file.
This is wrong:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
This is correct:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
I used <...> earlier as a placeholder style in explanation, but in the actual config file you must not include them.
So your fixed line should be:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:Network%405714@127.0.0.1:5432/prefect>"
Then run:
bash
systemctl daemon-reload
systemctl restart prefect-server
journalctl -u prefect-server -n 100 --no-pager
prefect version
If it worked, prefect version should change from:
text
Database: sqlite
to something like:
text
Database: postgresql
If you want, paste the output of these and I’ll verify it:
bash
systemctl show prefect-server -p Environment
prefect config view --show-secrets | grep DATABASE
prefect versionShubham
07/08/2026, 8:20 PMMarvin
07/08/2026, 8:20 PMMarvin
07/08/2026, 8:20 PMtext
sqlalchemy.exc.ProgrammingError
CREATE TABLE alembic_version ...
Prefect is attempting to create its migration table, but PostgreSQL is rejecting it.
Most likely causes:
- the prefect user does not have permission to create tables in the target database
- the database prefect exists, but ownership/privileges are wrong
- the connection is landing in a schema where the user lacks CREATE permission
What to check first
Please run:
bash
journalctl -u prefect-server -n 50 --no-pager
The important line is usually just above the SQL block and will say the exact PostgreSQL error, such as:
- permission denied for schema public
- permission denied for database prefect
- must be owner of schema public
That exact message matters.
Most common fix
Connect to PostgreSQL as a superuser and make sure the DB/user ownership is correct.
bash
sudo -u postgres psql
Then run:
sql
ALTER DATABASE prefect OWNER TO prefect;
GRANT ALL PRIVILEGES ON DATABASE prefect TO prefect;
\c prefect
ALTER SCHEMA public OWNER TO prefect;
GRANT ALL ON SCHEMA public TO prefect;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO prefect;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO prefect;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO prefect;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO prefect;
If the DB/user were not created yet, a cleaner setup is:
sql
CREATE USER prefect WITH PASSWORD 'Network@5714';
CREATE DATABASE prefect OWNER prefect;
\c prefect
GRANT ALL ON SCHEMA public TO prefect;
ALTER SCHEMA public OWNER TO prefect;
Then restart Prefect:
bash
systemctl restart prefect-server
journalctl -u prefect-server -n 100 --no-pager
prefect version
Quick direct permission test
You can test whether the user can create tables:
bash
psql "<postgresql://prefect:Network%405714@127.0.0.1:5432/prefect>"
Then:
sql
CREATE TABLE test_prefect_permissions(id int);
DROP TABLE test_prefect_permissions;
If CREATE TABLE fails, that confirms it is a PostgreSQL privilege issue, not a Prefect issue.
One more thing from your status output
Your ExecStart appears to show:
text
prefect server start --host 0.0.0.0 --port 4200>
That trailing > looks suspicious in the pasted output. If that is literally present in the service file, remove it so the command ends at:
ini
ExecStart=/home/prefect/.venv/bin/prefect server start --host 0.0.0.0 --port 4200
Useful references
- Prefect server docs: Prefect Server
- Prefect settings docs: Prefect settings
If you paste the full PostgreSQL error line from journalctl, I can tell you the exact SQL fix.Shubham
07/08/2026, 8:32 PMMarvin
07/08/2026, 8:32 PMMarvin
07/08/2026, 8:32 PMbash
psql "<postgresql://prefect:Network%405714@127.0.0.1:5432/prefect>"
Then use these queries.
1. Size of the Prefect database
sql
SELECT pg_size_pretty(pg_database_size('prefect')) AS db_size;
2. Size of all databases
sql
SELECT datname,
pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;
3. Largest tables in the Prefect DB
sql
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC
LIMIT 20;
4. More detailed table breakdown
sql
SELECT
relname AS table_name,
pg_size_pretty(pg_relation_size(C.oid)) AS table_size,
pg_size_pretty(pg_indexes_size(C.oid)) AS indexes_size,
pg_size_pretty(pg_total_relation_size(C.oid)) AS total_size
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname = 'public'
AND relkind = 'r'
ORDER BY pg_total_relation_size(C.oid) DESC
LIMIT 20;
At the Linux filesystem level
If PostgreSQL is local on the same host, you can also check where PostgreSQL stores data:
bash
sudo -u postgres psql -t -c "show data_directory;"
Then check disk usage there:
bash
du -sh /var/lib/postgresql
df -h
Or for the actual data directory path returned:
bash
du -sh /path/to/postgresql/data_directory
If you only care about Prefect's old SQLite file
Before migration, SQLite was usually under PREFECT_HOME, likely:
bash
/home/prefect/.prefect/prefect.db
Check it with:
bash
du -sh /home/prefect/.prefect/prefect.db
Useful note
After moving to PostgreSQL:
- Prefect metadata is stored in the PostgreSQL database
- flow/task logs may also contribute significantly depending on retention and logging volume
- result storage is usually separate unless you've configured DB-backed result persistence somewhere custom
If you want, I can give you:
1. a single SQL query for total Prefect DB usage, or
2. a query to find which Prefect tables grow the fastest.Shubham
07/08/2026, 8:35 PMMarvin
07/08/2026, 8:35 PMMarvin
07/08/2026, 8:36 PMsystemctl show ... Environment shows the environment variables for the systemd service process.
But when you run:
bash
prefect config view
prefect version
you are running Prefect in your current shell session, which only sees:
- shell environment variables
- Prefect profile settings
- defaults
It does not automatically inherit the environment from the prefect-server.service.
So:
- systemd service environment = what the server process uses
- interactive shell environment = what your CLI commands use
Those are separate unless you export the same variables in your shell.
That explains your output
Your shell shows only:
text
PREFECT_PROFILE='server'
PREFECT_API_AUTH_STRING=...
PREFECT_API_URL=...
So your shell-based prefect version is evaluating settings from the local profile/defaults, and it falls back to SQLite.
Meanwhile the running server may still be using PostgreSQL because its own systemd environment includes the DB URL.
Important warning
Your service environment still shows:
text
PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:prefectpassword@127.0.0.1:5432/prefect>
Those < > are still visible there.
If that is truly the literal value systemd loaded, it is suspicious. If the service is working anyway, double-check the actual unit file:
bash
cat /etc/systemd/system/prefect-server.service
If you still see:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://...>"
remove the angle brackets and restart the service.
It should be:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://prefect:prefectpassword@127.0.0.1:5432/prefect>"
Then:
bash
systemctl daemon-reload
systemctl restart prefect-server
How to make your CLI see the same DB config
In the same shell, export the variable manually:
bash
export PREFECT_API_DATABASE_CONNECTION_URL="<postgresql+asyncpg://prefect:prefectpassword@127.0.0.1:5432/prefect>"
Then run:
bash
prefect config view --show-secrets
prefect version
Now your CLI should report PostgreSQL too.
Best way to verify what the running server is actually using
Since your CLI and service differ, the most reliable checks are:
Option 1: inspect server logs
bash
journalctl -u prefect-server -n 100 --no-pager
Option 2: verify tables exist in PostgreSQL
bash
psql "<postgresql://prefect:prefectpassword@127.0.0.1:5432/prefect>" -c "\dt"
If Prefect is using PostgreSQL, you should see many Prefect tables there.
Option 3: check whether data is actively changing in PostgreSQL
bash
psql "<postgresql://prefect:prefectpassword@127.0.0.1:5432/prefect>" -c "SELECT COUNT(*) FROM flow_run;"
If you create new runs in the UI and that count increases, the server is using PostgreSQL.
Option 4: test whether SQLite file is no longer active
Check the old SQLite DB file timestamp/size:
bash
ls -lh /home/prefect/.prefect/prefect.db
If PostgreSQL is now the live DB, this file should stop changing.
Why prefect version is misleading here
prefect version is not asking the running server "what DB are you using?"
It is evaluating configuration in the current CLI process.
So in your situation:
- prefect version from shell → SQLite
- running server process via systemd → maybe PostgreSQL
That is normal when configs differ.
If you want CLI and service to match
You have a few options:
1. Export the env var in your shell each time
bash
export PREFECT_API_DATABASE_CONNECTION_URL="<postgresql+asyncpg://prefect:prefectpassword@127.0.0.1:5432/prefect>"
2. Put it in your shell profile
For example in `~/.bashrc`:
bash
export PREFECT_API_DATABASE_CONNECTION_URL="<postgresql+asyncpg://prefect:prefectpassword@127.0.0.1:5432/prefect>"
3. Store it in the active Prefect profileMarvin
07/08/2026, 8:36 PMprefect config view and prefect version do not match the running service
- to confirm PostgreSQL is truly live, check PostgreSQL tables directly, not just local CLI output
Please run these 3 commands and paste the output if you want me to confirm 100%:
bash
cat /etc/systemd/system/prefect-server.service
psql "<postgresql://prefect:prefectpassword@127.0.0.1:5432/prefect>" -c "\dt"
ls -lh /home/prefect/.prefect/prefect.dbShubham
07/08/2026, 8:48 PMMarvin
07/08/2026, 8:49 PMMarvin
07/08/2026, 8:51 PMini
ExecStart=/home/prefect/.venv/bin/prefect worker start --pool "docker-worker"
But prefect worker start still reads config from the normal Prefect settings sources, including:
- environment variables
- .env
- Prefect config/profile files
So if this machine already has a profile like:
- PREFECT_API_URL=<http://192.168.29.151:4200/api>
- PREFECT_API_AUTH_STRING=astra:Network@5714
then the worker can connect even though they are not written in the systemd unit.
Most likely explanation in your case
Earlier your CLI showed:
text
PREFECT_PROFILE='server'
PREFECT_API_AUTH_STRING='****' (from profile)
PREFECT_API_URL='<http://192.168.29.151:4200/api>' (from profile)
That strongly suggests the worker is inheriting the same local Prefect profile/config as the root user.
So:
- server service uses its own systemd env
- worker process likely uses ~/.prefect/profiles.toml or root user config
- that's why it works without explicit Environment= lines
Important distinction
These two are different:
- PREFECT_SERVER_API_AUTH_STRING
- used by the server to require Basic Auth on incoming requests
- PREFECT_API_AUTH_STRING
- used by clients/workers to send Basic Auth to the server
So if the server has:
ini
Environment="PREFECT_SERVER_API_AUTH_STRING=astra:Network@5714"
then the worker must somehow have:
ini
PREFECT_API_AUTH_STRING=astra:Network@5714
It does not need to be in the worker service file if it already exists in the active Prefect profile.
How to confirm exactly where the worker is getting it from
Run these as the same user that runs the worker (root in your case):
bash
prefect config view --show-secrets
and:
bash
cat /root/.prefect/profiles.toml
Also check whether shell env vars exist:
bash
env | grep PREFECT
How to see the worker service environment specifically
bash
systemctl show -p Environment --value prefect-worker | tr ' ' '\n'
If that comes back empty or without auth/url, then the worker is not getting config from systemd; it is getting it from Prefect profile/config files.
Recommended setup
Even if it works now, I recommend making the worker service explicit so it is predictable.
Example:
ini
[Unit]
Description=Prefect Worker
After=network.target
[Service]
User=root
WorkingDirectory=/home/prefect
Environment="PREFECT_API_URL=<http://192.168.29.151:4200/api>"
Environment="PREFECT_API_AUTH_STRING=astra:Network@5714"
ExecStart=/home/prefect/.venv/bin/prefect worker start --pool docker-worker
Restart=always
[Install]
WantedBy=multi-user.target
Then reload/restart:
bash
systemctl daemon-reload
systemctl restart prefect-worker
One more important thing
Your server service still appears to contain:
ini
Environment="PREFECT_API_DATABASE_CONNECTION_URL=<postgresql+asyncpg://...>"
Please remove the < > there if they are truly in the file.
Useful docs
- Prefect settings: Prefect settings
- Prefect workers: Prefect workers
- Prefect server: Prefect Server
If you want, I can help you inspect:
1. the active profiles.toml, and
2. a cleaned-up prefect-worker.service and prefect-server.service pair.