Agent configuration
Every agent and installer setting, the files it installs, and its commands.
The agent reads environment variables. On a server, systemd loads them from /etc/rowsafe/agent.env (owned by postgres, mode 0600). In Docker, from the agent service's env_file.
In agent.env, values are single-quoted, with no single quotes or newlines inside. After a change, run the installer again (it self-tests first) or sudo systemctl restart rowsafe-agent.
Where backups go
| Variable | Default | |
|---|---|---|
ROWSAFE_STORAGE | own | own: your bucket. rowsafe: Rowsafe Storage; Rowsafe gives the location and short-lived credentials, so the ROWSAFE_REPO_S3_* settings are unused (ROWSAFE_REPO_CIPHER_PASS still applies). |
Bucket (repository)
For your own bucket. The installer fills these in from the bucket URL and keys you paste; set them yourself for installs without questions.
| Variable | Default | |
|---|---|---|
ROWSAFE_REPO_S3_ENDPOINT | required | Host name without https://, e.g. <account-id>.eu.r2.cloudflarestorage.com. |
ROWSAFE_REPO_S3_BUCKET | required | A private bucket. |
ROWSAFE_REPO_S3_KEY | required | Access key ID of a token that can read and write only this bucket. |
ROWSAFE_REPO_S3_KEY_SECRET | required | Its secret. |
ROWSAFE_REPO_CIPHER_PASS | required | Encryption passphrase, 20+ characters. Store it in your secret manager before the first backup. |
ROWSAFE_REPO_S3_REGION | auto | auto for R2. |
ROWSAFE_REPO_S3_URI_STYLE | path | path or host. |
ROWSAFE_REPO_PATH_PREFIX | /rowsafe | Each database lives under <prefix>/<name>. |
ROWSAFE_REPO_S3_PORT | 443 | For other S3-compatible stores. |
ROWSAFE_REPO_S3_CA_FILE | system store | Absolute path to a PEM bundle to trust instead (e.g. a private MinIO). |
ROWSAFE_REPO_S3_VERIFY_TLS | true | false skips certificate checks. Tests only. |
Never change the passphrase, bucket or prefix of a repository that holds backups. See Change the configuration.
Second copy
Optional second bucket for the backups and change log, set up with --add-storage (guide). Every bucket setting above exists as ROWSAFE_REPO2_*, same defaults. Required: ROWSAFE_REPO2_S3_ENDPOINT, _S3_BUCKET, _S3_KEY, _S3_KEY_SECRET, _CIPHER_PASS. Optional: _S3_REGION, _S3_URI_STYLE, _PATH_PREFIX, _S3_PORT, _S3_CA_FILE, _S3_VERIFY_TLS.
| Variable | Default | |
|---|---|---|
ROWSAFE_REPO2_CIPHER_PASS | Its own passphrase, 20+ characters. Not the first bucket's. | |
ROWSAFE_REPO2_QUEUE_DIR | /var/lib/rowsafe/copy2-queue | Where change log files wait for the second bucket (at most 256 per database). |
If some are missing, the agent keeps using the first bucket only and says what's missing in its log and on the Storage page. Its pgBackRest configuration: /etc/rowsafe/pgbackrest/<name>.copy2.conf.
Connection to Rowsafe
| Variable | Default | |
|---|---|---|
ROWSAFE_ENROLL_TOKEN | One-time enrollment token (rse_...). Needed only until the first start; delete it after. | |
ROWSAFE_URL | https://api.rowsafe.sh | The API URL. Leave it unset. |
ROWSAFE_AUTO_UPDATE | true | false (or 0, no, off) turns self-update off. Any other value stops the agent from starting. Always off in Docker. |
ROWSAFE_POLL_INTERVAL | 5s | How often an idle agent asks for work. |
ROWSAFE_HEARTBEAT_INTERVAL | 30s | How often it reports in. |
Metrics (a server on its own)
Prometheus metrics for a server running Rowsafe on its own: see Prometheus and Grafana.
| Variable | Default | |
|---|---|---|
ROWSAFE_METRICS_LISTEN | (off) | Address to serve GET /metrics on, e.g. 127.0.0.1:9188. An address other than loopback needs ROWSAFE_METRICS_TOKEN. |
ROWSAFE_METRICS_TOKEN | Bearer token every scrape must send; at least 16 characters. |
PostgreSQL and the server
| Variable | Default | |
|---|---|---|
ROWSAFE_PG_USER | postgres | Role the agent connects as, over the Unix socket. Must be a superuser. |
ROWSAFE_PG_BIN_DIR | /usr/lib/postgresql/%d/bin | PostgreSQL server binaries, for Proof. %d is the major version. |
ROWSAFE_PGBACKREST_BIN | /usr/bin/pgbackrest | pgBackRest, also used in archive_command. |
ROWSAFE_DRILL_DIR | /var/lib/rowsafe/drills | Scratch space for Proof (internally a drill). |
ROWSAFE_DRILL_PORT | 55432 | Names the Unix socket of Proof's scratch cluster and of Rewind copies. They never listen on TCP. |
ROWSAFE_REWIND_DIR | /var/lib/rowsafe/rewind | Where Rewind restores copies. Needs about 1.3 × database size + 1 GiB free per copy. A short absolute path. |
ROWSAFE_DRILL_PRELOAD | auto | auto: start Proof's scratch cluster without production's shared_preload_libraries, retry with them if needed. production: always load them. |
ROWSAFE_COPIES_DIR | /var/lib/rowsafe/copies | Where migration previews and safe copies are restored, one directory each. |
ROWSAFE_COPY_PORTS | 55440-55539 | TCP ports safe copies listen on. Open them in your firewall for the addresses that connect. |
ROWSAFE_COPY_TLS_CERT, ROWSAFE_COPY_TLS_KEY | Certificate and key for safe copies, readable by the agent's user. Without them, each copy makes a self-signed one. | |
ROWSAFE_PREVIEW_KEEP | 1h | How long a preview copy is kept so the next preview starts fast (0: delete at once; at most 24h). |
ROWSAFE_RESTORE_POINT_TIMEOUT | 90s | How long to wait for a Mark to be archived, up to 4m. |
ROWSAFE_MONITORING | true | false turns metrics off. |
ROWSAFE_COLLECT_QUERY_TEXT | true | false sends long-running queries without their text. |
ROWSAFE_LOGS | true | false: the agent never reads PostgreSQL's log (Logs). |
ROWSAFE_STANDBY | on | Standbys. on: pairs with servers a person confirms in the dashboard. pinned: only with the fingerprints in ROWSAFE_STANDBY_PEERS, so even a compromised Rowsafe can't pair its own server. off: no standbys. |
ROWSAFE_STANDBY_PEERS | With pinned: comma-separated key fingerprints (sudo -u postgres rowsafe-agent key on each server) this server may exchange bucket settings with. | |
ROWSAFE_CREATE_CLUSTER_ALLOW_FILE | /etc/rowsafe/create-cluster-allowed | Forks: ports (ports 5440-5499) where root allowed new PostgreSQL clusters. Written by the installer (--allow-create-cluster). |
ROWSAFE_CREATED_CLUSTERS_FILE | /etc/rowsafe/created-clusters | Clusters created for forks (PORT UNIT per line), which Rowsafe may stop and start like those allowed with --allow-restart. Written by root. |
MongoDB
| Variable | Default | |
|---|---|---|
ROWSAFE_MONGODB_URI | A full connection string (mongodb://user:password@host:port/?authSource=admin) instead of the installer's saved login. For a single server; Docker sidecars usually use ROWSAFE_MONGODB_HOST and rowsafe-agent mongodb login. | |
ROWSAFE_MONGODB_HOST | 127.0.0.1 | Where MongoDB listens (in Docker, the mongo service's name). |
ROWSAFE_MONGODB_ARCHIVE_INTERVAL | 1m | How often new oplog entries go to your bucket (1s to 10m). |
ROWSAFE_MONGODB_BIN_DIR | PATH, then /usr/bin | Where mongodump, mongorestore and mongod are. |
ClickHouse
| Variable | Default | |
|---|---|---|
ROWSAFE_CLICKHOUSE_URL | http://127.0.0.1:PORT | ClickHouse's HTTP interface. Docker sidecars: e.g. http://clickhouse:8123. |
ROWSAFE_CLICKHOUSE_GATEWAY_LISTEN | 127.0.0.1:0 (a free port) | Where the agent listens, only during a backup or restore, for ClickHouse to write and read the backup. The agent encrypts every file before it goes to your bucket. The Docker image sets 0.0.0.0:9010. |
ROWSAFE_CLICKHOUSE_GATEWAY_URL | the listen address | How ClickHouse reaches that address. Docker sidecars: e.g. http://rowsafe-agent:9010. |
ROWSAFE_CLICKHOUSE_DATA_PATH | ClickHouse's own path | Where the agent reads ClickHouse's data folder (read-only) to copy each new part, if it's not at the server's path. Not needed for Docker sidecars (data volume at /var/lib/clickhouse). |
ROWSAFE_CLICKHOUSE_ARCHIVE_INTERVAL | 15s | How often the agent looks for new parts (1s to 5m). Changes how soon a change reaches your bucket, not restore precision (change times come from ClickHouse). |
Redis and Valkey
| Variable | Default | |
|---|---|---|
ROWSAFE_REDIS_HOST | 127.0.0.1 | Where Redis or Valkey is. Docker sidecars: e.g. redis. |
ROWSAFE_REDIS_DATA_DIR | Redis's own data folder | Where the agent reads the snapshot file (read-only), only when Rowsafe can't follow the server. Docker sidecars: the data volume's mount in the agent, e.g. /srv/redis-data. |
Files
| Variable | Default | |
|---|---|---|
ROWSAFE_RESTIC_BIN | /usr/local/lib/rowsafe/restic | restic, for Files. Falls back to restic on the PATH. |
ROWSAFE_FILES_CACHE_DIR | <state dir>/files-cache | restic's cache. |
ROWSAFE_FILES_STAGING_DIR | <state dir>/files-staging | Where restores are staged before going in place. The root helper expects the default. |
ROWSAFE_FILES_ALLOW_FILE | /etc/rowsafe/files-allowed | The exact folders root allowed Rowsafe to read and restore into, with their owner (PATH UID lines; --files PATH --allow-files). The agent only reads it. |
ROWSAFE_FILES_MOUNT_DIR | /rowsafe-files | Docker: where volumes to back up are mounted. |
The installer, systemd unit and update guard assume the default directories (ROWSAFE_STATE_DIR, ROWSAFE_CONFIG_DIR, ROWSAFE_LOG_DIR, ROWSAFE_INSTALL_DIR). Don't change them on a server.
Connection pooling
For Connection pooling (PgBouncer). All optional. On a server, leave all but the first two unset: the root helper and its units use the default paths. Paths must be absolute.
| Variable | Default | |
|---|---|---|
ROWSAFE_POOLER_STATS_URL | A PgBouncer Rowsafe doesn't manage, to monitor: its admin console as a postgres:// URL (e.g. postgres://stats:PASSWORD@pgbouncer:6432/pgbouncer in Docker). Rowsafe reads its stats and never changes it. | |
ROWSAFE_POOLER_DATABASE | With ROWSAFE_POOLER_STATS_URL: the Rowsafe name of the database it serves. Only if the agent has several databases. | |
ROWSAFE_POOLER_ALLOW_FILE | /etc/rowsafe/pooler-allowed | PostgreSQL ports where root allowed PgBouncer in front. Written by the installer (--allow-pooler). |
ROWSAFE_POOLER_DIR | <ROWSAFE_STATE_DIR>/pooler | Where the agent writes requests for the root helper. |
ROWSAFE_POOLER_RESULT_DIR | /run/rowsafe-pooler | Where the root helper writes answers. |
ROWSAFE_POOLER_SOCKET_DIR | /var/run/postgresql | PgBouncer's Unix socket directory, for its admin console. |
ROWSAFE_POOLER_USERLIST | /etc/pgbouncer/userlist.txt | PgBouncer's user list, where the agent reads rowsafe_pgbouncer's password. |
MySQL and MariaDB
| Variable | Default | |
|---|---|---|
ROWSAFE_MYSQL_USER, ROWSAFE_MYSQL_PASSWORD_FILE | not set | An account to use instead of Rowsafe's own (rowsafe@localhost). Needs the same privileges. |
ROWSAFE_MYSQL_ADMIN_USER, ROWSAFE_MYSQL_ADMIN_PASSWORD_FILE | root, not set | Docker: an administrator the agent logs in as once, to create its account. |
ROWSAFE_MYSQL_BIN_DIR | not set | Where to look first for xtrabackup, mariadb-backup, mysqlbinlog, mysqld...; then PATH, /usr/sbin, /usr/bin. |
ROWSAFE_MYSQL_CONF_FILE | /etc/rowsafe/mysql/server.cnf | Rowsafe's option file for binary log settings (none: Rowsafe can't change the configuration). Not used in Docker. |
ROWSAFE_MYSQL_SCRATCH_MEMORY | 256M | InnoDB buffer pool of Proof's and Rewind's private servers. |
ROWSAFE_MYSQL_PART_SIZE_MB | 64 | Upload part size. Covers backups up to about 600 GiB; raise it for larger (16 to 2048). |
Docker
Run the agent in Docker. The agent images (ghcr.io/rowsafe/agent:pg<major>[-alpine] is the newest release; :<version>-pg<major>[-alpine] pins one) set:
| Variable | Image default | |
|---|---|---|
ROWSAFE_MODE | docker-sidecar | native outside the image. |
ROWSAFE_SPOOL_DIR | /rowsafe-spool | Spool volume shared with PostgreSQL. Each database uses <dir>/<name>. |
ROWSAFE_SPOOL_STALL_AFTER | 5m | How long the oldest spooled file may wait before it counts as an archiving failure (at least 1m). |
ROWSAFE_STATE_DIR | /var/lib/rowsafe | Identity and state: the rowsafe-state volume. |
ROWSAFE_CONFIG_DIR | /var/lib/rowsafe/pgbackrest | Generated pgBackRest configuration, in the state volume. |
ROWSAFE_DRILL_DIR | /var/lib/rowsafe/drills | Proof's scratch space: about 1.3 × database size + 1 GiB free. |
ROWSAFE_AUTO_UPDATE | false | Always off in Docker. |
ROWSAFE_RESTIC_BIN | /usr/local/bin/restic | restic 0.19.1, checked against its release's SHA-256 at image build. |
ROWSAFE_FILES_MOUNT_DIR | /rowsafe-files | Mount volumes to back up with the database here (read-only is enough). |
ROWSAFE_FORK_TARGET_DIR | Makes this sidecar a fork target: absolute path of the empty data directory (the postgres container's PGDATA) a fork is restored into. | |
ROWSAFE_DOCKER_CONTROL_SOCKET | /run/rowsafe-control/control.sock | Where the optional container control service listens (rowsafe-control volume). |
ROWSAFE_IMAGE_VARIANT | the image's tag, e.g. pg17 | Which agent image this is; Update now picks the same one from the new release. ClickHouse images (clickhouse26.8...) run with ROWSAFE_MODE=native; this tells the agent it's in Docker, so it never updates itself. |
The container control service (ghcr.io/rowsafe/docker-control:latest, or :<version>) reads:
| Variable | Default | |
|---|---|---|
ROWSAFE_CONTROL_SERVICE | postgres | The compose service (in its own project) it may stop, start and restart. |
ROWSAFE_CONTROL_CONTAINER | Instead: one container name (without compose). | |
ROWSAFE_CONTROL_ALLOW_UIDS | 999,70 | Users allowed to connect: the agent's. |
ROWSAFE_CONTROL_STOP_TIMEOUT | 2m | How long PostgreSQL may take to shut down before Docker kills it (at most 10m). |
ROWSAFE_CONTROL_SOCKET | /run/rowsafe-control/control.sock | Where it listens. |
ROWSAFE_CONTROL_ALLOW_AGENT_UPDATE | 0 | 1: also let Rowsafe replace the agent's container with a newer signed image when someone clicks Update now (how). |
ROWSAFE_CONTROL_UPDATE_ONLY | 0 | 1: only update the agent's container; stopping, starting or restarting the database container is refused. Turns agent updates on. Used for ClickHouse. |
ROWSAFE_CONTROL_AGENT_SERVICE | rowsafe-agent | The agent's compose service, for agent updates. |
ROWSAFE_CONTROL_AGENT_CONTAINER | Instead: the agent container's name (without compose). |
Installer
curl -fsSL https://rowsafe.sh | sudo sh # install, on its own (no dashboard; sudo rowsafe login later)
curl -fsSL https://rowsafe.sh | sudo sh -s -- --account # install and connect: approve the server in your browser
curl -fsSL https://rowsafe.sh | sudo sh -s rse_... # the same with an enrollment token: nothing to approve
curl -fsSL https://rowsafe.sh | sudo sh -s rse_... --protect app # without questions (automation)
curl -fsSL https://rowsafe.sh | sudo sh # run again: update, re-test, start
curl -fsSL https://rowsafe.sh | sudo sh -s -- --uninstall # options alone go after `sh -s --`https://rowsafe.sh/install serves the same script.
Permissions. The --allow-NAME options answer the permission questions: --allow-NAME says yes, --no-allow-NAME says no. Without a terminal or an option, what touches only the database is allowed (restart, create-cluster, updates, pooler, tuning, sqlite-modes, files); the server's security updates, reboot, firewall and public pooler aren't. Some need another: create-cluster, updates and security-updates need restart; reboot needs security-updates; pooler-public needs pooler. Without it they're refused, and turning one off turns off what needs it. Change them later with sudo rowsafe-allow.
| Option | |
|---|---|
rse_... | The enrollment token (or ROWSAFE_ENROLL_TOKEN=rse_...). Without one, on a terminal, you approve the server in your browser from a printed link. |
--protect NAME | Set up the database without questions: register as NAME, plan, apply. Never restarts the database. Storage settings must be in the environment (ROWSAFE_REPO_*). |
--no-setup | Install the agent and storage only. Add the database later from the dashboard or with rowsafe adopt. |
--install-postgres VERSION | Fresh Debian or Ubuntu: install and start PostgreSQL VERSION (13 to 18) from apt.postgresql.org (signing key checked). Refused if PostgreSQL is installed; re-runs keep the one it installed. With --protect, restarts that new PostgreSQL once if backups need it. Used by Create a server for me. |
--listen-public | Make PostgreSQL reachable from the network: every address, TLS (self-signed certificate in /etc/ssl/rowsafe-postgresql) and scram-sha-256 for every network login; local rules unchanged. Put a firewall in front: it decides who can connect. Restarts PostgreSQL only if --install-postgres installed it or you say yes; otherwise it applies at the next restart. |
--files PATH | Back up the folder PATH (uploads, media) with the database, without asking; repeatable. Gives the agent's user read-only access (ACL) if needed. Files. |
--allow-files | Default for folders named with --files. Let Rowsafe put restored files back into those folders only (as the folder's owner), listed in /etc/rowsafe/files-allowed. A folder added later in the dashboard needs --files with that folder once more. Only when someone asks and confirms. |
--no-allow-files | Don't allow it, without asking. |
--no-files | Don't ask about upload folders. |
--allow-pooler | Default on. Let Rowsafe install and manage the connection pooler (PgBouncer; ProxySQL for MySQL and MariaDB; chproxy for ClickHouse). Nothing is installed until someone turns pooling on and confirms. |
--no-allow-pooler | Don't allow it. A PgBouncer Rowsafe set up keeps running, but Rowsafe can't change it. |
--allow-pooler-public | With --allow-pooler: also let PgBouncer listen on every address when someone picks "public". Otherwise only this server and its private network. |
--no-allow-pooler-public | PgBouncer back to this server and its private network only. |
--allow-pooler-target ADDRESS:PORT | MySQL and MariaDB: let ProxySQL send connections to that server (the new primary after a standby's promotion). --no-allow-pooler-target ADDRESS:PORT stops it. |
--allow-restart | Default on. Let Rowsafe restart or stop the database: Restart and Rewind the whole database in the dashboard, only when someone clicks and confirms. Installs the root helper rowsafe-pg-restart. |
--no-allow-restart | Don't allow it, and remove the helper. Also turns off updates, security updates, reboots and new clusters for forks. |
--allow-updates | Default on. Let Rowsafe install minor updates and upgrade the database, only when someone clicks Update or Upgrade and confirms. Needs --allow-restart. Updates. |
--no-allow-updates | Don't allow it. |
--allow-security-updates | Default off. Let Rowsafe install the server's security updates. Needs --allow-restart. |
--no-allow-security-updates | Don't allow it. |
--allow-reboot | Default off. Let Rowsafe reboot the server. Needs --allow-security-updates. |
--no-allow-reboot | Don't allow it. --no-allow-security-updates turns it off too. |
--allow-firewall | Default off. Let Rowsafe limit who reaches the database's port with nftables: only that port, never SSH, only when someone picks the addresses under Security and confirms. |
--no-allow-firewall | Don't allow it; removes Rowsafe's firewall rule and helper. |
--allow-tuning | Default on. MongoDB, ClickHouse, Redis and Valkey: let Rowsafe write settings changed under Tuning into its own settings file. --no-allow-tuning turns it off. |
--mysql-standby | MySQL and MariaDB: allow standbys with this server. Its account gets administrator rights, used only when someone adds, promotes or removes a standby, forks to an empty server or moves a database in, and confirms. --no-mysql-standby: don't. |
--mongodb-standby | MongoDB: allow standbys. Its user gets clusterManager; root's helper may hand out the replica set's key file and add Rowsafe's lines to mongod.conf (a copy kept). Needs --allow-restart. |
--mongodb-clones | MongoDB: keep an empty server ready for forks and move-ins (Rowsafe's user gets restore and readWriteAnyDatabase). |
--clickhouse-clones | ClickHouse: keep an empty server ready for forks and move-ins (Rowsafe's user may create and drop databases). |
--mongodb-replica-set | MongoDB: turn a standalone server into a one-member replica set without asking (one MongoDB restart). Needed to restore to any second. |
--no-mongodb-replica-set | MongoDB: never do that. |
--allow-create-cluster | Default on where restarts are. Let Rowsafe create a PostgreSQL cluster (free port in 5440-5499) when someone forks a database here and confirms. Debian and Ubuntu (pg_createcluster). |
--no-allow-create-cluster | Don't allow it. Clusters created earlier stay. |
--setup-storage | Set up (or change) backup storage, even if configured. |
--storage PROVIDER | Skip the "where" question: rowsafe (Rowsafe Storage; also works without a terminal), or r2, b2, s3, wasabi, spaces, s3-compatible. |
--no-prompt | Never ask, even on a terminal. |
--check-storage | Test the storage in agent.env (your bucket, or Rowsafe Storage through the agent, plus any second copy). Changes nothing. |
--add-storage | Add a second copy in another bucket (questions like the first), or move it. Without a terminal, takes ROWSAFE_REPO2_* from the environment and tests it before saving. |
--remove-second-copy | Stop sending backups to the second copy. Its bucket is left as is. |
--download-only DIR | Download and verify the agent into DIR; install nothing. No root needed. |
--permissions | Where Rowsafe is installed: change only what it may do (--allow-NAME, --no-allow-NAME, --no-allow-files), offline, then print what it may do. What sudo rowsafe-allow runs. Exits 0 done, 2 refused and nothing changed (a needed permission is off, or not possible here), 1 a change failed. |
--uninstall | Stop and remove the service and /opt/rowsafe. Keeps configuration, state and logs. |
--uninstall --purge | Also delete /etc/rowsafe, /var/lib/rowsafe, /var/log/rowsafe and the logrotate file. Refused while PostgreSQL still archives with Rowsafe's configuration. |
-h, --help | Help. |
| Environment variable | |
|---|---|
ROWSAFE_VERSION=1.2.3 | Install exactly this version, not the channel's latest. |
ROWSAFE_CHANNEL=stable | Channel to install from. Later updates follow the host's channel in Rowsafe. |
ROWSAFE_ALLOW_DOWNGRADE=1 | Allow ROWSAFE_VERSION older than the installed one. |
ROWSAFE_ALLOW_UNSUPPORTED_OS=1 | Try an OS other than Debian 12/13 or Ubuntu 22.04/24.04. |
ROWSAFE_MONGODB_ADMIN_USER, ROWSAFE_MONGODB_ADMIN_PASSWORD | Without a terminal: a MongoDB admin, to create Rowsafe's MongoDB user. Used once, never saved. |
ROWSAFE_CLICKHOUSE_ADMIN_USER, ROWSAFE_CLICKHOUSE_ADMIN_PASSWORD | Without a terminal: a ClickHouse admin, to create Rowsafe's ClickHouse user when a users file can't be used. Used once, never saved. |
The installer copies these from its environment into agent.env, without printing them: ROWSAFE_URL, ROWSAFE_ENROLL_TOKEN, ROWSAFE_STORAGE, every ROWSAFE_REPO_* and ROWSAFE_REPO2_*, ROWSAFE_AUTO_UPDATE, ROWSAFE_PG_USER, ROWSAFE_PG_BIN_DIR, ROWSAFE_PGBACKREST_BIN, ROWSAFE_DRILL_DIR, ROWSAFE_DRILL_PORT, ROWSAFE_POLL_INTERVAL, ROWSAFE_HEARTBEAT_INTERVAL. Set anything else by editing the file.
What the installer does:
- Checks the system: root, Debian 12/13 or Ubuntu 22.04/24.04, amd64 or arm64, a
postgresuser and PostgreSQL server binaries, OpenSSL 3. - Downloads the release manifest and signature, verifies the Ed25519 signature with its built-in key, then the binary's size and SHA-256. Any failure: nothing changes.
- Installs pgBackRest if missing (apt.postgresql.org if configured, else your distribution). Installs restic 0.19.1 for Files from GitHub into
/usr/local/lib/rowsafe/restic, checked against its published SHA-256 (a mismatch installs nothing; falls back to your distribution's restic 0.17+ if GitHub is unreachable). - Installs the agent, the systemd unit and a logrotate file.
- Sets up storage if needed and writes
agent.env, keeping existing settings. - If settings are missing, enables the service without starting it and lists what's missing. Otherwise self-tests the new agent, then starts or restarts it.
- Unless
--no-setup, finds the database (PostgreSQL, MySQL, MariaDB, MongoDB, ClickHouse, Redis or Valkey) and protects it (questions below, or--protect NAME).
The questions it asks
A first install on a terminal asks these. Shown for PostgreSQL; other databases name themselves and skip what doesn't apply (by database).
| Question | |
|---|---|
| Where should backups go? | Only when Rowsafe Storage is offered. 1 (default): Rowsafe Storage, bucket questions skipped. 2: your own bucket. |
| Paste your bucket URL | E.g. Cloudflare R2's "S3 API" URL, https://<account-id>.r2.cloudflarestorage.com/<bucket>. Provider, endpoint, region and bucket are detected from it. Set up backup storage. |
| Access key ID, secret | Typed hidden. Then it tests the bucket: writes, reads back and deletes a small file. |
| Encryption passphrase | Generated and shown once (or your own). You type its last 4 characters to confirm you saved it. |
Name it in Rowsafe [app]: | After Found PostgreSQL 18 on port 5432 (1.2 GB; databases: app). Enter accepts. |
Turn on backups for app now? [Y/n] | After the plan, in plain words. Nothing changes until yes. |
Allow restarting PostgreSQL from the Rowsafe dashboard? [Y/n] | Only when someone clicks Restart and confirms. |
Allow Rowsafe to install and manage PgBouncer? ... [Y/n] | Nothing installed now: only when someone turns connection pooling on and confirms. |
Allow Rowsafe to install PostgreSQL updates when you click Update? [Y/n] | Only if restarts are allowed. Minor updates and major upgrades, only when someone confirms; a Mark first. |
Allow Rowsafe to install this server's security updates when you click Install? [y/N] | Only if restarts are allowed. |
Allow Rowsafe to reboot this server when you click Reboot? [y/N] | Only after yes to security updates. |
Allow Rowsafe to limit who can reach PostgreSQL's port with the firewall? [y/N] | Only if nftables is installed. Only PostgreSQL's port, never SSH; only when someone picks the addresses and confirms. The firewall. |
Allow Rowsafe to create a new PostgreSQL cluster here when you fork a database to this server? [Y/n] | For forks, only when someone confirms, on ports 5440-5499. Debian and Ubuntu, only if restarts are allowed. |
Restart PostgreSQL now? [y/N] | Only if a setting needs it. Yes: restarts, checks changes reach the bucket, starts the first full backup. No: restart when it suits you; Rowsafe notices. |
Does this app store uploads on this server? Rowsafe found /srv/app/storage/app (2.3 GiB, ...). Back it up with app, so a restore brings back both? [Y/n] | For up to three folders that look like uploads. Files. |
Allow Rowsafe to put restored files back into /srv/app/storage/app, as the folder's owner? [Y/n] | Once a folder is protected; names exactly the folders it allows. Only when someone asks and confirms. |
Secrets are never printed. Nothing is saved until the storage questions are answered. Without a terminal, or with --no-prompt, it asks nothing: put storage settings in the environment and use --protect NAME. Installer changes appear in the audit log as installer on <hostname>.
Files on the server
/opt/rowsafe/ postgres 0755
rowsafe-agent -> versions/<v>/... the binary systemd runs; swapped on update
versions/<v>/rowsafe-agent current and previous version
bin/rowsafe-agent-guard root 0755; rolls back an update that keeps crashing
/etc/rowsafe/ root:postgres 0750
agent.env postgres 0600; settings and secrets
pgbackrest/<name>.conf postgres 0600; generated, local edits are overwritten
/var/lib/rowsafe/ postgres 0700; identity, state, running Proof copies
files.json the protected folders' settings and state (Files)
files-cache/, files-staging/ restic's cache; restores staged before they are put in place
files-restored/<id>/ restored files Rowsafe could not put back into their folder
storage-credentials.json postgres 0600; Rowsafe Storage only: the current short-lived credentials
/var/log/rowsafe/ postgres 0700; pgBackRest logs, rotated daily, 14 kept
/usr/local/lib/rowsafe/restic root 0755; restic, for Files
/etc/rowsafe/files-allowed root 0644; "PATH UID" per folder Rowsafe may read and restore into (--allow-files)
/etc/systemd/system/rowsafe-agent.service
/etc/logrotate.d/rowsafe
/usr/local/sbin/rowsafe-allow root 0755; see and change what Rowsafe may do (sudo rowsafe-allow)
/usr/local/lib/rowsafe/install.sh root 0755; root's copy of the installer, checked against the signed manifest
Only when you allowed restarts, updates, security updates or reboots:
/etc/rowsafe/restart-allowed root 0644; "PORT UNIT" per cluster Rowsafe may restart or stop
/etc/rowsafe/updates-allowed root 0644; postgresql, security, reboot: what Rowsafe may do
/usr/local/lib/rowsafe/rowsafe-pg-restart root 0755; the root helper
/etc/systemd/system/rowsafe-pg-restart.{service,path} restarts, stops, starts
/etc/systemd/system/rowsafe-pg-update.{service,path} updates, upgrades, reboots
/var/lib/rowsafe-pg-restart/ root 0700; what an upgrade keeps for undo- Pooler (
--allow-pooler):/etc/rowsafe/pooler-allowedand therowsafe-poolersystemd units. PgBouncer's files: Connection pooling. - Logs:
journalctl -u rowsafe-agent(JSON lines) and/var/log/rowsafe/<name>-<command>.log. Each task's log, with pgBackRest's output, is also inrowsafe task ID. - The systemd unit runs the agent as
postgreswithNoNewPrivileges, a read-only system, a private/dev, no capabilities and restricted address families. The installer rewrites it: put local changes in a drop-in (sudo systemctl edit rowsafe-agent). - The files helper (
--allow-files):rowsafe-files-helper.{service,path}in/etc/systemd/system, with a drop-in (rowsafe-files-helper.service.d/folders.conf) that lets it write only into the folders in/etc/rowsafe/files-allowed. Same script as the root helper, in its own sandbox; private copy and cooldowns in/var/lib/rowsafe-files-helper. Files. - The firewall helper (
--allow-firewall):/usr/local/lib/rowsafe/rowsafe-firewall(root 0755), started byrowsafe-firewall.pathwhen the agent asks, loaded at boot byrowsafe-firewall-restore.service. Changes only its own nftables table (inet rowsafe), only for ports in/etc/rowsafe/firewall-allowed(root 0644); rules kept in/var/lib/rowsafe-firewall.
Agent commands
rowsafe-agent run enroll if needed, then run tasks (what systemd runs)
rowsafe-agent inspect print what the agent sees in PostgreSQL (read-only)
[--port 5432] [--socket-dir /var/run/postgresql] [--user postgres]
rowsafe-agent selftest check the configuration, pgBackRest, Rowsafe and every database
rowsafe-agent storage test [--wait 60s]
write, read back and delete a test file in the backup storage
rowsafe-agent health container health check (Docker)
rowsafe-agent files discover|access|list|add
the folders that go with a database (used by the installer)
rowsafe-agent mongodb status|login|initiate|save-uri --port 27017 [--admin-user NAME]
MongoDB: what the installer needs to know; create Rowsafe's
MongoDB user; start a replica set of one; save a login you made
rowsafe-agent clickhouse status|login|save-login --port 8123 [--users-xml | --admin-user NAME]
ClickHouse: what the installer needs to know; create Rowsafe's
ClickHouse user (--users-xml prints a users.d file for root to
install; --admin-user creates it with SQL, password on stdin);
save a login you made (user:password on stdin)
rowsafe-agent clickhouse download-backup --stanza FOLDER [--label LABEL --to DIR]
ClickHouse: list a database's backups, or decrypt one (and the full
backup it builds on) into DIR for RESTORE ... FROM File(...)
rowsafe-agent unseal decrypt a file Rowsafe wrote to your bucket (stdin to stdout)
rowsafe-agent versionRun the self-test exactly as the service would:
sudo systemd-run --quiet --wait --pipe --collect -p User=postgres \
-p EnvironmentFile=/etc/rowsafe/agent.env /opt/rowsafe/rowsafe-agent selftest