Postgres without a DBA · Part 1 of 3
PostgreSQL without a DBA: ten checks, in the order they hurt
Durability first, memory tuning last. Ten read-only checks for a self-managed PostgreSQL server, ordered by what they can break.
Without a database administrator, the first risk on a PostgreSQL server is not a slow query. It is a setting or a state that can lose committed data, stop automatic cleanup, or fill the disk without warning. Check for those first and tune memory last. Ten read-only checks cover that ground, asked as five questions in order of blast radius.
A PostgreSQL health check is a read-only pass over settings, system catalogs, and statistics views that finds what can lose data, fill the disk, or stop cleanup before users notice. The built-in pg_monitor role is enough to run it. It needs no superuser and reads no table rows.
In short
- Ask five questions in this order: can the server lose committed data, can automatic cleanup stop, can the disk fill, can you see what is running, and is the server slower than its hardware allows.
- Ten read-only queries answer them under a role with the built-in
pg_monitorrole. Each section gives one query, what a bad result means, and what to do. - One pass by hand finds today's problems. Guardioz runs these ten check groups about once an hour on self-managed PostgreSQL 18 and shows the measurement behind each finding. Test Guardioz on your server.
The order: five questions, ten checks
Order the checks by what a failure costs, not by the order of postgresql.conf. A wrong memory setting makes a server slow. A wrong durability setting can lose committed data. A forgotten replication slot can fill the disk and take the database offline. So the first three questions decide whether the database survives, and the last one decides how fast it runs.
| ID | Check | Question | Where to look | Guardioz severity |
|---|---|---|---|---|
| C1 | fsync is on | 1. Lose committed data | pg_settings | Critical |
| C2 | autovacuum and track_counts are on | 2. Cleanup stops | pg_settings, table options | Critical |
| C3 | No transaction holds back cleanup | 2. Cleanup stops | pg_stat_activity | Warning or Suggestion |
| C4 | No table waits too long for cleanup | 2. Cleanup stops | pg_stat_user_tables | Warning |
| C5 | No replication slot holds WAL | 3. Disk fills | pg_replication_slots | Critical, Warning, or Suggestion |
| C6 | WAL archiving works | 3. Disk fills | pg_stat_archiver | Critical, Warning, or Suggestion |
| C7 | track_activities is on | 4. Blind | pg_settings | Warning |
| C8 | No invalid indexes | 5. Slower than it should be | pg_index | Critical or Warning |
| C9 | No duplicate indexes | 5. Slower than it should be | pg_index | Critical or Warning |
| C10 | Eight settings fit the server | 5. Slower than it should be | pg_settings, statistics views | Warning or Suggestion |
The ten checks match the ten check groups that Guardioz documents. That is deliberate: the list is short enough to run by hand, and each item is a condition that builds up silently until users notice.
Set up a read-only role for the checks
Run every check as a dedicated login with the built-in pg_monitor role. The PostgreSQL 18 documentation says pg_monitor “allows reading/executing various monitoring views and functions” and is a member of pg_read_all_settings, pg_read_all_stats, and pg_stat_scan_tables. Source: Predefined roles.
CREATE ROLE health_check LOGIN PASSWORD 'choose-a-long-password';
GRANT pg_monitor TO health_check;
That role reads every setting and every pg_stat_* view, including the sessions of other users. It does not read the rows of your tables, and it needs no superuser. Every query below is a SELECT or SHOW. None of them changes anything.
Guardioz uses the same pattern. Its installer creates a role named guardioz_monitor, grants it pg_monitor, and the agent connects over the local Unix socket in read-only sessions. Source: Guardioz requirements.
1. Can this server lose committed data?
One setting decides whether a committed transaction survives a crash. Check it first.
C1. fsync is on
With fsync on, PostgreSQL makes sure that updates reach the disk physically. The PostgreSQL 18 documentation warns that turning it off “can result in unrecoverable data corruption in the event of a power failure or system crash,” and that it is only advisable “if you can easily recreate your entire database from external data.” Source: WAL configuration.
SELECT name, setting, source FROM pg_settings WHERE name = 'fsync';
A bad result is off. The source column tells you where the value came from: the configuration file, the command line, or ALTER SYSTEM.
Fix it now, then find out who turned it off and why:
ALTER SYSTEM SET fsync = on;
SELECT pg_reload_conf();
In Guardioz: the Reliability settings check, at Critical. It reports the current value and the statement that turns fsync back on.
2. Can automatic cleanup stop?
PostgreSQL never overwrites a row in place. Updates and deletes leave dead rows behind, and autovacuum removes them. Three things stop that cleanup: autovacuum switched off, a transaction that stays open, and a table so large that the default trigger fires too late.
C2. autovacuum and track_counts are on
autovacuum runs the launcher that starts cleanup. The documentation adds that “track_counts must also be enabled for autovacuum to work,” because autovacuum decides from the statistics it collects. Both are on by default. Sources: Vacuuming, Statistics.
SELECT name, setting FROM pg_settings WHERE name IN ('autovacuum', 'track_counts');
-- tables where someone switched autovacuum off by hand
SELECT c.oid::regclass AS table_name
FROM pg_class c
WHERE c.relkind IN ('r', 'm')
AND EXISTS (SELECT 1 FROM pg_options_to_table(c.reloptions) o
WHERE o.option_name = 'autovacuum_enabled' AND NOT o.option_value::boolean);
A bad result is off for either setting, or any table in the second list. With autovacuum off, PostgreSQL still launches vacuums to prevent transaction ID wraparound, but routine cleanup of dead rows stops, so tables and indexes keep growing.
ALTER SYSTEM SET autovacuum = on;
ALTER SYSTEM SET track_counts = on;
SELECT pg_reload_conf();
ALTER TABLE my_table RESET (autovacuum_enabled);
In Guardioz: the Autovacuum check, at Critical. It checks the two settings. Per-table switches are outside that check, so the second query is worth running by hand.
C3. No transaction holds back cleanup
A transaction that stays open blocks cleanup on every table. The PostgreSQL 18 documentation says that “an open transaction prevents vacuuming away recently-dead tuples that may be visible only to this transaction,” so remaining idle for a long time “can contribute to table bloat.” The usual cause is an application that opens a transaction and waits, which shows as idle in transaction. Source: Client connection defaults.
SELECT pid, datname, usename, application_name, state,
now() - xact_start AS open_for,
left(query, 60) AS last_statement
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
AND backend_type = 'client backend'
AND pid <> pg_backend_pid()
ORDER BY xact_start
LIMIT 10;
Look at the oldest rows. A transaction open for minutes during a batch job can be normal. One open for hours is a bug in the application or a forgotten session. The statement text appears only while track_activities is on (C7).
Fix the application first: commit or roll back promptly. For a stuck session, SELECT pg_terminate_backend(pid); ends it and rolls its transaction back. As a guard, set idle_in_transaction_session_timeout. Its default is 0, which disables it. Prepared transactions and replication slots can hold back cleanup too, as the documentation on routine vacuuming notes.
In Guardioz: the Open transactions and cleanup check, at Warning or Suggestion. It compares one collection with the next, so its first verdict arrives about an hour after install.
C4. No table waits too long for cleanup
Autovacuum starts on a table when dead rows pass a threshold: autovacuum_vacuum_threshold (50 by default) plus autovacuum_vacuum_scale_factor (0.2 by default) times the table size. On a large table, 20% is a lot of dead rows. PostgreSQL 18 adds autovacuum_vacuum_max_threshold, a cap on that number with a default of 100,000,000 rows. Sources: Vacuuming, PostgreSQL 18 release notes.
With the defaults, a table of one billion rows waits for 200 million dead rows before PostgreSQL 17 vacuums it. PostgreSQL 18 caps the wait at 100 million. A table of 100 million rows still waits for 20 million.
SELECT schemaname, relname, n_live_tup, n_dead_tup, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;
SELECT name, setting FROM pg_settings
WHERE name IN ('autovacuum_vacuum_threshold', 'autovacuum_vacuum_scale_factor',
'autovacuum_vacuum_max_threshold');
Read the first query against the second. A table near the top with n_dead_tup in the millions and an old last_autovacuum is waiting for a trigger it rarely reaches. Lower the trigger for that table only, with a storage parameter such as ALTER TABLE big_table SET (autovacuum_vacuum_scale_factor = ...), and leave the server default alone.
In Guardioz: the Tables behind on cleanup check, at Warning. Where it can calculate a safe value, the finding carries the statement that lowers the trigger for that table.
3. Can the disk fill without warning?
Two features keep WAL files on disk on purpose: replication slots and WAL archiving. When either one stalls, pg_wal grows until something stops it. The PostgreSQL 18 documentation is direct about the end state: “If the file system containing pg_wal/ fills up, PostgreSQL will do a PANIC shutdown. No committed transactions will be lost, but the database will remain offline until you free some space.” Source: Continuous archiving.
C5. No replication slot holds WAL
A replication slot keeps every WAL file its consumer has not read yet. With the default max_slot_wal_keep_size of -1, “replication slots may retain an unlimited amount of WAL files.” A slot left behind by a removed replica or an old logical replication test keeps WAL forever. Source: Replication configuration.
SELECT slot_name, slot_type, database, active, wal_status,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_held
FROM pg_replication_slots
WHERE NOT temporary
ORDER BY restart_lsn NULLS LAST;
A bad result is a slot with active = false and a large wal_held. The wal_status column says how far it went: extended means max_wal_size is exceeded but the files are still kept, and lost means the slot is no longer usable. Source: pg_replication_slots.
If nothing consumes the slot, drop it with SELECT pg_drop_replication_slot('slot_name');. The consumer then has to be set up again. To cap the damage in future, set max_slot_wal_keep_size, knowing that a standby that falls further behind loses the WAL it needs.
In Guardioz: the Replication slots check, at Critical, Warning, or Suggestion. It flags physical and logical slots that are inactive or hold too much WAL.
C6. WAL archiving works
With archive_mode on, PostgreSQL keeps each WAL file until the archiver copies it away. If archive_command and archive_library are both empty, archiving is paused, “but the server continues to accumulate WAL segment files.” If the command keeps failing, “the pg_wal/ directory will continue to fill with WAL segment files until the situation is resolved.” Sources: WAL configuration, Continuous archiving.
SELECT current_setting('archive_mode') AS archive_mode,
current_setting('archive_command') <> ''
OR current_setting('archive_library') <> '' AS archiver_set,
(SELECT count(*) FROM pg_ls_archive_statusdir()
WHERE name LIKE '%.ready') AS files_waiting,
(SELECT min(modification) FROM pg_ls_archive_statusdir()
WHERE name LIKE '%.ready') AS oldest_waiting_since,
last_archived_time, failed_count, last_failed_time
FROM pg_stat_archiver;
A bad result is archive_mode on with archiver_set false, a growing files_waiting, an old oldest_waiting_since, or a recent last_failed_time. The function pg_ls_archive_statusdir() is available to pg_monitor by default. Source: System administration functions.
Read the server log for the archive command's error, fix the destination, and watch files_waiting fall. If archiving is not needed at all, turn archive_mode off, which needs a restart.
In Guardioz: the Transaction log archiving check, at Critical, Warning, or Suggestion. It reports an archiver that has stopped, fails, or falls behind, and archive_mode on without a command or library.
4. Can you see what is running?
When something goes wrong, the first question is what the server is running right now. One setting decides whether PostgreSQL can answer it.
C7. track_activities is on
track_activities “enables the collection of information on the currently executing command of each session.” It is on by default, and only superusers, roles with pg_read_all_stats, and the session owner can see the data. Source: Statistics.
SELECT name, setting FROM pg_settings WHERE name = 'track_activities';
With it off, pg_stat_activity shows sessions without their statements, and the open-transaction check in C3 loses its evidence. Turn it on with ALTER SYSTEM SET track_activities = on; and SELECT pg_reload_conf();.
In Guardioz: the Monitoring visibility check, at Warning. The finding says what you stop seeing while it is off.
5. Is the server slower than its hardware allows?
Only now look at speed. Two index problems waste work on every write, and eight settings decide how well PostgreSQL uses the machine.
C8. No invalid indexes
An index becomes invalid when CREATE INDEX CONCURRENTLY or REINDEX CONCURRENTLY fails. The PostgreSQL 18 documentation says such an index “will be ignored for querying purposes because it might be incomplete,” yet “it will still consume update overhead.” A failed unique index also “continues to enforce its uniqueness constraint afterwards.” Source: CREATE INDEX.
SELECT n.nspname AS schema, t.relname AS table_name, c.relname AS index_name,
i.indisvalid, i.indisready, i.indislive, i.indisprimary, i.indisunique,
pg_size_pretty(pg_relation_size(c.oid)) AS size
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_class t ON t.oid = i.indrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE NOT (i.indisvalid AND i.indisready AND i.indislive)
ORDER BY pg_relation_size(c.oid) DESC;
Any row is a finding. The documented recovery is to drop the index and build it again, or to run REINDEX INDEX CONCURRENTLY. Three rules from the Guardioz checks reference make that choice safer:
- Rebuild an index that backs a primary key. Never drop it.
- Before you rebuild a unique index, find and resolve the duplicate rows. A rebuild fails while they exist, and each failed attempt leaves one more unusable copy.
- Indexes named with
_ccnewor_ccoldare leftovers of an interruptedREINDEX CONCURRENTLY. Drop them only whenpg_stat_progress_create_indexshows no rebuild running on that table.
In Guardioz: the Invalid indexes check, at Critical or Warning. Each index gets its own page with the evidence and the statement to run: rebuild, drop, or drop the owning constraint.
C9. No duplicate indexes
A duplicate index costs disk space and slows every insert and update on its table, and the planner gains nothing from it. Count only exact copies: the same columns or expressions, the same index type, operator classes, collations, sort order, NULLS NOT DISTINCT setting, and partial-index condition. An index on one column and another on that column plus a second one are not duplicates.
SELECT i.indrelid::regclass AS table_name,
array_agg(i.indexrelid::regclass
ORDER BY i.indisprimary DESC, i.indisunique DESC,
i.indexrelid::regclass::text) AS identical_indexes,
pg_size_pretty(sum(pg_relation_size(i.indexrelid))) AS total_size
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
WHERE i.indisvalid AND i.indisready AND i.indislive
GROUP BY i.indrelid, c.relam, i.indkey::text, i.indclass::text, i.indcollation::text,
i.indoption::text, i.indnullsnotdistinct,
coalesce(pg_get_expr(i.indexprs, i.indrelid), ''),
coalesce(pg_get_expr(i.indpred, i.indrelid), '')
HAVING count(*) > 1;
Keep the first index in each row. The query lists an index that backs a primary key or a unique constraint first. Save the definition of each copy with SELECT pg_get_indexdef('index_name'::regclass);, then drop it with DROP INDEX CONCURRENTLY, which does not block reads or writes.
In Guardioz: the Duplicate indexes check, at Critical or Warning. It names the index to keep, says why, and gives the statement that puts a dropped copy back.
C10. Eight settings fit the server
Eight settings decide most of how PostgreSQL uses memory, WAL, and processors. Their right values depend on the machine and, for three of them, on the measured workload. The table lists what each value depends on, from the inputs that the Guardioz performance tuning check documents.
| ID | Setting | What a good value depends on | Restart needed |
|---|---|---|---|
| S1 | shared_buffers | Total RAM, the current value, the total size of the monitored databases, and how much data the server pushes out of its cache to make room for new reads | Yes |
| S2 | work_mem | Total RAM, the current shared_buffers, max_connections, and the temporary files that queries write to disk | No |
| S3 | maintenance_work_mem | Total RAM, the processor count, the largest table and the largest B-tree index, autovacuum_work_mem, and the number of autovacuum workers | No |
| S4 | effective_cache_size | Total RAM and the current value | No |
| S5 | max_worker_processes | The processor count and the current value | Yes |
| S6 | max_wal_size | How much WAL the server writes, how often a checkpoint was requested instead of started by the timer, checkpoint_timeout, checkpoint_completion_target, and free space on the data disk | No |
| S7 | checkpoint_completion_target | The current value | No |
| S8 | log_hostname | The current value, and whether log lines carry the client host name (log_line_prefix, log_destination, logging_collector) | No |
SELECT name, setting, unit, source, pending_restart
FROM pg_settings
WHERE name IN ('shared_buffers', 'effective_cache_size', 'work_mem', 'maintenance_work_mem',
'max_worker_processes', 'max_wal_size', 'checkpoint_completion_target',
'log_hostname')
ORDER BY name;
Read source to see which values anyone ever changed, and pending_restart to see changes still waiting for a restart. The defaults for max_wal_size and checkpoint_completion_target are 1 GB and 0.9. Two deep dives on the Guardioz blog cover single settings: max_wal_size, explained and the logging setting that slows down new connections.
A RAM-based rule gets four of these eight close enough. The others need counters from the running server. Part 2 sorts tuning tools by whether they read those counters, and Part 3 shows what an AI assistant misses when it sees only the hardware.
In Guardioz: the Performance tuning check, at Warning or Suggestion. Each setting gets a recommended value, a confidence percentage, and an ALTER SYSTEM SET statement. shared_buffers and max_worker_processes carry a Requires restart badge.
What these ten checks do not cover
Ten checks are a floor, not a full audit. This list mirrors the Not covered today section of the Guardioz checks reference. A database administrator checks these too, so run them by hand.
| ID | Gap | How to look by hand | Source |
|---|---|---|---|
| X1 | Transaction ID wraparound | Run SELECT datname, age(datfrozenxid) FROM pg_database; and compare with autovacuum_freeze_max_age, 200 million by default. Near the limit PostgreSQL accepts only read-only transactions. | Routine vacuuming |
| X2 | Backups that restore | Restore the latest backup to another server and query it. A backup nobody restored is a hope, not a backup. | Continuous archiving |
| X3 | Free disk space | Run SHOW data_directory;, then df -h on that path and on the disk that holds pg_wal. | Continuous archiving |
| X4 | Slow queries and plans | Install pg_stat_statements. It must be in shared_preload_libraries, so it needs a restart. | pg_stat_statements |
| X5 | Locks and blocked sessions | Query pg_stat_activity with pg_blocking_pids(pid) to see who waits for whom. | System administration functions |
| X6 | Replica lag | On the primary, read replay_lag in pg_stat_replication. | Monitoring statistics |
| X7 | Unused and missing indexes | Unused: idx_scan = 0 in pg_stat_user_indexes, counted since the last statistics reset and only on this server. Missing: needs query analysis. | Monitoring statistics |
| X8 | Table and index bloat | Measure with the pgstattuple extension. It reads the whole relation, so run it off-peak. | pgstattuple |
| X9 | Uptime, checksum failures, and operating-system settings | Use an external uptime monitor. Read checksum_failures in pg_stat_database. Check kernel memory and huge-page settings with your operating-system tools. | Monitoring statistics |
Wraparound deserves the same weight as question 1. The documentation says that with fewer than three million transactions left before wraparound, the server refuses to assign new transaction IDs, and “only read-only transactions can be started.” Source: Routine vacuuming.
Run them once by hand, or every hour
A pass by hand tells you how the server looks today. It does not tell you what changed since last week, which finding matters most, or whether a value fits the workload. Those answers need history.
| ID | Question | One pass by hand | Guardioz, about once an hour |
|---|---|---|---|
| H1 | Coverage | The queries on this page | The same ten check groups, with every passing check listed as OK |
| H2 | History | None. Each run starts from zero. | Snapshots over time. max_wal_size and a raise of work_mem need at least two collections. |
| H3 | Priority | You rank the results | Critical, Warning, or Suggestion on each finding |
| H4 | Evidence | The raw query output | The measurement behind each recommendation, with a confidence percentage that grows as history accumulates |
| H5 | Fix | You write the statement | A statement to copy, with restart needs flagged. You run it yourself. |
| H6 | Load | One run, when you remember | Read-only queries one at a time, each with a time limit, about once an hour |
Guardioz is not an uptime monitor and not real-time monitoring. It does not tell you that the server stopped, and it draws no second-by-second graphs. Source: How Guardioz works.
Test Guardioz on your server
Guardioz runs these ten check groups about once an hour and shows the measurement behind each finding, with the statement to fix it.
It fits today if you run PostgreSQL 18 yourself, on Linux with systemd or on FreeBSD, and point it at a primary server.
- Book 30 minutes with Dr. Kot to try it on one of your servers.
- Write to the Guardioz team, led by Alicja Kucharczyk, at contact@guardioz.com.
- Start free: one user, up to three servers, no card. Or open the live demo without signing up.
On an older PostgreSQL or a managed service such as Amazon RDS or Azure Database for PostgreSQL? Guardioz does not support it yet. Write to contact@guardioz.com with your version and platform.
Key facts at a glance
Defaults and quotes from the PostgreSQL 18 documentation and the Guardioz documentation, checked on November 4, 2026.
| ID | Fact | Value | Source |
|---|---|---|---|
| K1 | fsync off | “can result in unrecoverable data corruption in the event of a power failure or system crash” | WAL configuration |
| K2 | autovacuum and track_counts | Both on by default. Autovacuum needs track_counts to work. | Vacuuming, Statistics |
| K3 | Autovacuum vacuum trigger | 50 rows plus 20% of the table, capped by autovacuum_vacuum_max_threshold at 100,000,000 rows, new in PostgreSQL 18 | Vacuuming, Release 18 |
| K4 | idle_in_transaction_session_timeout | 0 by default, which disables it | Client defaults |
| K5 | max_slot_wal_keep_size | -1 by default: slots may retain an unlimited amount of WAL | Replication |
| K6 | pg_wal file system full | PANIC shutdown. No committed transactions lost, database offline until space is freed. | Continuous archiving |
| K7 | max_wal_size, checkpoint_completion_target | 1 GB and 0.9 by default | WAL configuration |
| K8 | autovacuum_freeze_max_age | 200 million transactions by default. New XIDs refused with fewer than three million left before wraparound. | Routine vacuuming |
| K9 | pg_monitor | Member of pg_read_all_settings, pg_read_all_stats, and pg_stat_scan_tables | Predefined roles |
| K10 | Guardioz scope | PostgreSQL 18 run yourself on Linux with systemd or FreeBSD, primaries only, collected about once an hour. Free plan: one user, up to three servers. | Requirements, Pricing |
Frequently asked questions
What should I check first on a PostgreSQL server I inherited?
Check what can lose data or stop the server before you look at speed. In order: fsync is on, autovacuum and track_counts are on, no transaction stays open for hours, no replication slot or failing archiver keeps WAL on the disk, and track_activities is on. Then look for invalid and duplicate indexes and review eight memory, WAL, worker, and logging settings. A role with pg_monitor can run every check read-only.
Is it ever safe to turn fsync off in PostgreSQL?
Only when you can rebuild the whole database from another source. The PostgreSQL 18 documentation says that turning fsync off can result in unrecoverable data corruption after a power failure or system crash, and that it is only advisable if you can easily recreate your entire database from external data. On a production server, keep it on.
Is it safe to turn off autovacuum in PostgreSQL?
No, not on a production server. Without autovacuum, dead rows from updates and deletes stay in tables and indexes until someone runs VACUUM by hand. PostgreSQL still launches vacuums to prevent transaction ID wraparound, but routine cleanup stops. Autovacuum also needs track_counts, which collects the statistics it decides from.
What does idle in transaction mean in PostgreSQL?
It means a session opened a transaction and is waiting for the client, with the transaction still open. The PostgreSQL 18 documentation warns that an open transaction prevents vacuuming away recently-dead tuples and can contribute to table bloat, and it may also hold locks. The setting idle_in_transaction_session_timeout ends such sessions after a time you choose. Its default, zero, disables it.
Why is my PostgreSQL disk filling up with WAL files?
The two common causes are a replication slot that no consumer reads and a WAL archiver that fails or falls behind. With the default max_slot_wal_keep_size of -1, a slot may retain an unlimited amount of WAL. A failing archive command leaves pg_wal to fill until the problem is fixed. If the file system that holds pg_wal fills up, PostgreSQL does a PANIC shutdown and stays offline until you free space.
How do I find invalid indexes in PostgreSQL?
Query pg_index for indexes where indisvalid, indisready, or indislive is false. An index usually ends up invalid when CREATE INDEX CONCURRENTLY or REINDEX CONCURRENTLY fails. Queries ignore it, but it still adds write overhead, and an invalid unique index still enforces uniqueness. Recover with REINDEX INDEX CONCURRENTLY, or drop it and build it again.
What privileges does a PostgreSQL health check need?
The built-in pg_monitor role is enough for the checks on this page. It is a member of pg_read_all_settings, pg_read_all_stats, and pg_stat_scan_tables, so it can read every setting and every pg_stat view, including other users' sessions. It does not give access to the rows of your tables. No superuser is needed.
How often should I run a PostgreSQL health check?
Run the full pass after every configuration change, upgrade, or failover, and on a schedule in between. The conditions on this list build up over hours or days. That is why Guardioz collects about once an hour instead of every few seconds, according to its documentation.
Do I need a DBA to run PostgreSQL in production?
Not for routine health. You need one named person who owns these ten checks and acts on the findings. A PostgreSQL expert still pays off for query tuning, major upgrades, replication design, and restore drills.
Sources
PostgreSQL 18 documentation, checked on November 4, 2026.
- Write Ahead Log configuration (fsync, max_wal_size, archive_command)
- Automatic vacuuming configuration
- Run-time statistics (track_activities, track_counts)
- Client connection defaults (idle_in_transaction_session_timeout)
- Replication configuration (max_slot_wal_keep_size)
- Resource consumption
- Predefined roles (pg_monitor)
- System administration functions (pg_ls_archive_statusdir)
- CREATE INDEX (building indexes concurrently)
- Routine vacuuming (transaction ID wraparound)
- pg_replication_slots
- Continuous archiving
- The cumulative statistics system
- pg_stat_statements
- pgstattuple
- Release 18
Guardioz, checked on November 4, 2026.