Resources

How to back up Managed PostgreSQL to Object Storage

Published on: October 14, 2026

This guide describes how to set up a Cloud Server that dumps a Managed PostgreSQL database with pg_dump every hour, saves the dumps in a Managed Object Storage bucket, and removes dumps that are older than seven days. It also describes how to restore a dump into a new service.

The dumps are independent of the service's automatic backups, and you can keep them for longer than the backup retention. For the automatic backups, see Managed PostgreSQL backups.

Before you start

You need:

This guide covers the following operating systems, installed from the UpCloud templates:

  • Ubuntu 22.04, 24.04, and 26.04
  • Debian 12 and 13
  • AlmaLinux 8, 9, and 10
  • Rocky Linux 8, 9, and 10
  • Fedora 43

Connect the services to the network

The database accepts connections only from networks attached to it. Attach the database and the Object Storage instance to the network. If the database is already attached to an SDN Private Network, use that network and attach only the Object Storage instance. Attaching takes a few minutes. Switching an existing database from the Utility network takes longer, because the service is rebuilt. When both are running again, create a Cloud Server on the same network, so that it gets the routes to them when it starts. If the server already exists, restart it. Run the commands in the rest of this guide as root on that server.

  • New database: when you create it, click Attach private network under Connection access and select the network.
  • Existing database on the Utility network: on its Overview page, under Connection, click Switch to SDN Networks and select the network. The switch rebuilds the service with its data, and the database can't be switched back to the Utility network afterwards.
  • Object Storage instance: on its Overview page, under Private access, click Attach private network, select the network, and enter a name.

Install the PostgreSQL client and rclone

Install the client for your service's PostgreSQL major version, for example 18, and rclone, and create the folder for rclone's configuration file.

apt-get update && apt-get install -y curl ca-certificates
install -d /usr/share/postgresql-common/pgdg
curl -fsSo /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc
. /etc/os-release
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt ${VERSION_CODENAME}-pgdg main" > /etc/apt/sources.list.d/pgdg.list
apt-get update && apt-get install -y postgresql-client-<major-version> rclone
mkdir -p /root/.config/rclone

Save the connection details

  1. Save the database's service URI in a file that only root can read. In the Control Panel, the service URI is the Connection string under Connection on the database's Overview page. Click SHOW_PASSWORD in it to show the password before you copy it. In the API, it's the service's service_uri. The dump contains the database named in the URI, defaultdb by default. If your data is in another database, replace defaultdb in the URI with its name.

    install -m 600 /dev/null /etc/pg-backup.env
    echo "DATABASE_URL='<service-uri>'" > /etc/pg-backup.env
  2. Save the following as /root/.config/rclone/rclone.conf, with the user's access keys and the instance's private endpoint:

    [dumps]
    type = s3
    provider = Other
    access_key_id = <access-key>
    secret_access_key = <secret-key>
    endpoint = https://<private-endpoint>
    region = <region>
  3. Check that the server reaches the bucket. The command lists the bucket's contents, or prints nothing for an empty bucket, without an error:

    rclone lsf dumps:<bucket>

Schedule the backup

  1. Save the backup script as /usr/local/bin/pg-backup:

    #!/bin/bash
    set -euo pipefail
    source /etc/pg-backup.env
    name=<database-name>-$(date -u +%Y%m%dT%H%M%SZ).dump
    pg_dump --format=custom "$DATABASE_URL" | rclone rcat dumps:<bucket>/$name.partial
    rclone moveto dumps:<bucket>/$name.partial dumps:<bucket>/$name
    rclone delete --min-age 7d dumps:<bucket>

    Replace <database-name> with the name of the database you back up. It's used only in the dump file names. Each dump is saved as a .partial file first and gets its final name only when pg_dump succeeds. The last line removes dumps older than seven days. To keep them longer, for example for longer than the database's backup retention, change 7d.

  2. Save the service as /etc/systemd/system/pg-backup.service:

    [Unit]
    Description=Back up PostgreSQL to Object Storage
    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/pg-backup
  3. Save the timer as /etc/systemd/system/pg-backup.timer:

    [Unit]
    Description=Back up PostgreSQL every hour
    [Timer]
    OnCalendar=hourly
    Persistent=true
    [Install]
    WantedBy=timers.target
  4. Make the script executable, start the timer, and run the first backup:

    chmod 755 /usr/local/bin/pg-backup
    systemctl daemon-reload
    systemctl enable --now pg-backup.timer
    systemctl start pg-backup.service

Check the dumps

rclone lsl dumps:<bucket>

A .partial file in the list is a backup that failed or is still running. If a dump is missing or a .partial file stays there, run journalctl -u pg-backup.service to see why.

Restore a dump

Create a new Managed PostgreSQL service for the restore. Then, on a server on a network that's attached to both the new service and the Object Storage instance, set up the PostgreSQL client and rclone as above and run:

rclone cat dumps:<bucket>/<file> | pg_restore --no-owner --no-privileges --dbname='<target-service-uri>'
Contributed by: Samir Haliru

Can't find what you're looking for?

For more help you can contact our awesome 24/7 support team