How to back up a managed database (DigitalOcean, AWS RDS)
A managed database's automatic backups protect you from a failed node, not from a deleted cluster, a lost account or a mistake you notice after the retention window. Keep your own logical dumps as well: run pg_dump or mysqldump from a server the database trusts, over TLS, against the direct connection rather than a connection pool, and store the files outside the provider account.
What the provider's backups do
Both providers back up the whole cluster or instance and can restore it to a point in time. The details differ:
| DigitalOcean Managed Databases | AWS RDS DB instance | |
|---|---|---|
| Schedule | A full backup once a day, at a time DigitalOcean sets and you cannot change | Daily, during the instance's backup window |
| Retention | 7 days | 0 to 35 days. Default 7 in the console, 1 from the API or CLI. 0 turns backups off. |
| Point-in-time restore | Any point in the last 7 days, PostgreSQL and MySQL | Any point in the retention period. Transaction logs reach S3 every five minutes. |
| A restore creates | A new cluster | A new DB instance |
| Deleting the database | Destroys its backups | Deletes automated backups unless you choose to retain them. Final and manual snapshots stay. |
Retained RDS automated backups still expire at the end of their retention period; a final snapshot does not. Google Cloud SQL can take a final backup when you delete an instance, kept 30 days by default. Separately, it keeps a deleted instance's backups for four days, and recovering from those means contacting Google Cloud Customer Care.
Choosing a managed database? DigitalOcean's are one of the options this guide covers.
Create a DigitalOcean accountAffiliate link — we earn a commission if you sign up.
What they don't cover
- Mistakes found late. On DigitalOcean the window is seven days. A bad migration noticed on day eight cannot be undone from the provider's backups.
- The account. Backups sit in the same account as the database. Whoever can delete the cluster can take its backups with it, and if you lose access to the account you lose both.
- Small restores. A provider restore brings back the whole cluster as a new one. To recover one table, you restore everything, copy the table out and delete the copy.
- Portability. Provider backups restore into the same provider's service. A dump is a file you can load into any server that runs the same engine.
So keep the provider's backups for fast whole-cluster recovery, and add a nightly dump stored outside the account. That is the 3-2-1 rule applied to a database you don't run yourself.
Run the dump from a server the database trusts
Managed databases accept connections only from sources you allow. Pick a server in the same region, ideally on the same private network, and allow it in:
- DigitalOcean: the cluster's trusted sources, in the control panel or with doctl. The rule type can be
droplet,k8s,ip_addr,tagorapp. - AWS RDS: the DB instance's VPC security group. Add an inbound rule for port 5432 (PostgreSQL) or 3306 (MySQL) with the server's security group or IP address as the source.
doctl databases firewalls append <cluster-id> --rule droplet:<droplet-id>Copy the host and port from the cluster's connection details. DigitalOcean clusters typically take client connections on port 25060.
Connect directly, not through a pool
DigitalOcean PostgreSQL clusters can have PgBouncer connection pools, each with its own connection details. Pools default to transaction mode, which hands each transaction to whichever server connection is free. pg_dump needs one session for the whole dump, and DigitalOcean's documentation warns that running it against a transaction-mode pool causes errors. Use the cluster's own connection details, not a pool's.
Install a client that matches the server
pg_dump must be the same major version as the server or newer. It refuses to dump a newer server rather than risk a broken dump. Compare the two:
psql "host=<db-host> port=25060 dbname=mydb user=doadmin sslmode=require" -Atc "show server_version"pg_dump --versionOn Ubuntu, the PostgreSQL project's apt repository has every supported client version. Its setup script adds the repository, then you install the client that matches your server:
sudo apt install -y postgresql-commonsudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.shsudo apt install -y postgresql-client-17For MySQL, use MySQL's own mysqldump from the same major version as the server, for example an 8.4 client for an 8.4 server.
Create a read-only backup user
Don't dump as the admin user (doadmin on DigitalOcean, the master user on RDS). A read-only user can't damage anything if its password leaks. PostgreSQL 14 and newer have a predefined role for exactly this:
CREATE ROLE backup WITH LOGIN PASSWORD 'long-random-password';
GRANT pg_read_all_data TO backup;pg_read_all_data reads every table, view and sequence in every schema. It does not bypass row-level security, and pg_dump stops with an error on a table with RLS policies unless the user can bypass them. If your admin user can't grant the role, grant per schema instead. Run the ALTER DEFAULT PRIVILEGES lines as the role that creates your tables, because default privileges only apply to objects the current role creates:
GRANT USAGE ON SCHEMA public TO backup;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO backup;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO backup;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO backup;For MySQL, --routines needs the global SELECT privilege, so grant on *.*:
CREATE USER 'backup'@'%' IDENTIFIED BY 'long-random-password';
GRANT SELECT, SHOW VIEW, TRIGGER, EVENT ON *.* TO 'backup'@'%';That is enough with the mysqldump options below: --no-tablespaces removes the need for PROCESS, and --set-gtid-purged=OFF the need for RELOAD or FLUSH_TABLES. The trusted sources or security group still decide which machines can connect.
Dump PostgreSQL over TLS
Put the password in ~/.pgpass of the user that runs the dump, one line per connection, and make the file private:
<db-host>:25060:mydb:backup:long-random-passwordchmod 600 ~/.pgpasspg_dump "host=<db-host> port=25060 dbname=mydb user=backup sslmode=verify-full sslrootcert=/etc/ssl/certs/db-ca.crt" -Fc -w -f mydb.dumpsslmode=requireencrypts the connection but does not check who answered. It is what DigitalOcean's copied connection details use by default.sslmode=verify-fullalso checks the certificate chain and that the host name matches. For DigitalOcean Standard Edition clusters, download the CA certificate from the cluster's Overview page. For RDS, use AWS's bundle,https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem.- With libpq 16 or newer,
sslrootcert=systemtrusts the system's CA store instead of a file. DigitalOcean's Advanced Edition clusters need it forverify-full. -Fcwrites a compressed custom-format archive;-wnever prompts for a password, so a cron job fails at once instead of hanging.
RDS for PostgreSQL 15 and newer rejects unencrypted connections by default, because rds.force_ssl is on. See the pg_dump guide for formats and compression.
Dump MySQL over TLS
Keep the connection settings and password in an option file, readable only by its owner:
[client]
user=backup
password="long-random-password"
host=<db-host>
port=25060
ssl-mode=VERIFY_IDENTITY
ssl-ca=/etc/ssl/certs/db-ca.crtsudo chmod 600 /etc/mysql/managed-backup.cnfmysqldump --defaults-extra-file=/etc/mysql/managed-backup.cnf --single-transaction --set-gtid-purged=OFF --no-tablespaces --routines --events --triggers mydb | gzip > mydb.sql.gzssl-mode=VERIFY_IDENTITYrefuses an unencrypted connection and checks the CA and host name.REQUIREDonly encrypts. MySQL's default,PREFERRED, falls back to plain text if TLS fails.--set-gtid-purged=OFF: DigitalOcean's import docs say to set it, or the import may fail. Without it, a dump from a server with GTIDs setssql_log_binandgtid_purgedon the target, which needs privileges a managed admin may not have. Keep those statements only if the restored server will become a replica.--no-tablespacesleaves out tablespace statements, so the user needs noPROCESSprivilege.--single-transactionreads InnoDB tables from one consistent snapshot without locking them.
A pipe returns gzip's exit status, so a failed dump still looks like success. In scripts, start with set -o pipefail.
Restore a dump into a new managed database
Create the new cluster or instance on the same major version or newer, then the empty database. The admin user on a managed service is not a real superuser: DigitalOcean says its managed PostgreSQL doesn't give full superuser access, and the RDS master user is created NOSUPERUSER. So restore without ownership and grant commands:
pg_restore --no-owner --no-privileges -d "host=<new-host> port=25060 dbname=mydb user=doadmin sslmode=require" mydb.dump--no-owner makes the restoring user own every object, and --no-privileges skips GRANT and REVOKE for roles that may not exist yet. To carry roles over, add --no-role-passwords to pg_dumpall --globals-only. It then reads pg_roles instead of pg_authid, which only superusers can read; set the passwords again afterwards. See backing up PostgreSQL roles.
For MySQL, create the database and load the dump:
mysql --defaults-extra-file=/etc/mysql/new-cluster.cnf -e "CREATE DATABASE mydb"gunzip < mydb.sql.gz | mysql --defaults-extra-file=/etc/mysql/new-cluster.cnf mydbDigitalOcean MySQL clusters created after 8 April 2020 require a primary key on every new table. Tables without one fail with error 3750, Unable to create or change a table without a primary key. Add primary keys before you need the restore, or turn the requirement off with a configuration request through DigitalOcean's API.
Schedule it and keep a copy elsewhere
This script dumps to a temporary name, reads the whole archive back to prove it is complete, renames it and keeps seven days. It runs as root, so the password goes in /root/.pgpass.
#!/usr/bin/env bash
set -euo pipefail
CONN="host=<db-host> port=25060 dbname=mydb user=backup sslmode=verify-full sslrootcert=/etc/ssl/certs/db-ca.crt"
BACKUP_DIR="/var/backups/managed-db"
KEEP_DAYS=7
OUT="$BACKUP_DIR/mydb-$(date +%Y-%m-%d_%H%M).dump"
mkdir -p "$BACKUP_DIR"
trap 'rm -f "$OUT.partial"' EXIT
pg_dump "$CONN" -Fc -w -f "$OUT.partial"
pg_restore -f /dev/null "$OUT.partial"
mv "$OUT.partial" "$OUT"
find "$BACKUP_DIR" -name 'mydb-*.dump' -type f -mtime +"$KEEP_DAYS" -delete30 2 * * * root /usr/local/bin/managed-pg-backup.sh >> /var/log/managed-pg-backup.log 2>&1Make the script executable with chmod 755. Then copy /var/backups/managed-db to storage in a different account or provider, for example with rclone. Once a month, restore a dump into a scratch database and compare row counts, as in testing a restore.
Common errors
| Error | Fix |
|---|---|
Connection timed out | The server is not a trusted source, or the security group has no inbound rule for it. Add it. |
no pg_hba.conf entry for host "...", user "...", database "...", SSL off | The server requires TLS. Add sslmode=require or stricter. |
aborting because of server version mismatch | pg_dump is older than the server. Install the matching postgresql-client package. |
| Errors only when connecting through a pool | pg_dump can't run through a transaction-mode pool. Use the cluster's direct connection details. |
pg_dumpall fails with permission denied on pg_authid | Add --no-role-passwords. |
Unable to create or change a table without a primary key | The target requires primary keys (DigitalOcean MySQL default). Add them, or have the requirement turned off. |
Access denied; you need (at least one of) the ... privilege(s) for this operation while loading a MySQL dump | Dump again with --set-gtid-purged=OFF. If it fails on a view, trigger or routine, its DEFINER is another account: create that account first. |
Frequently asked questions
- Are managed database backups enough?
- They cover hardware failure and recent mistakes. They live in the same account, go away with the database on DigitalOcean (and on RDS unless you retain them), and reach back 7 days on DigitalOcean or up to 35 on RDS. Add your own dumps stored elsewhere.
- What happens to RDS backups when I delete the DB instance?
- Automated backups are deleted unless you choose to retain them, and retained ones still expire with their retention period. Final and manual snapshots stay until you delete them.
- Can I run pg_dump through PgBouncer?
- Not in transaction mode; DigitalOcean's docs say it causes errors. Connect to the cluster directly.
- Does restoring a managed database backup overwrite the existing one?
- No. On DigitalOcean and RDS a restore creates a new cluster or instance, and you point your app at it.
How this was checked
Commands, limits and prices were checked against these official pages, on October 3, 2026:
- DigitalOcean: How to Manually Restore PostgreSQL Database Clusters from Backups
- DigitalOcean: How to Manually Restore MySQL Database Clusters from Backups
- DigitalOcean: PostgreSQL Features
- DigitalOcean: How to Manage Connection Pools for PostgreSQL Database Clusters
- DigitalOcean: How to Connect to PostgreSQL Database Clusters
- DigitalOcean: How to Secure PostgreSQL Managed Database Clusters
- DigitalOcean: How to Migrate from Managed to Self-Managed PostgreSQL
- DigitalOcean: How to Import MySQL Databases into DigitalOcean Managed Databases
- DigitalOcean: MySQL Limits
- DigitalOcean: doctl databases firewalls append
- Amazon RDS User Guide: Introduction to backups
- Amazon RDS User Guide: Backup retention period
- Amazon RDS User Guide: Retaining automated backups
- Amazon RDS User Guide: Restoring a DB instance to a specified time
- Amazon RDS User Guide: Controlling access with security groups
- Amazon RDS User Guide: Using SSL with a PostgreSQL DB instance
- Amazon RDS User Guide: Using SSL/TLS to encrypt a connection to a DB instance
- Amazon RDS User Guide: Understanding the rds_superuser role
- Google Cloud SQL: About Cloud SQL backups
- PostgreSQL documentation: SSL Support (libpq)
- PostgreSQL documentation: Database Connection Control Functions
- PostgreSQL documentation: pg_dump
- PostgreSQL documentation: pg_restore
- PostgreSQL documentation: pg_dumpall
- PostgreSQL documentation: Predefined Roles
- PostgreSQL documentation: ALTER DEFAULT PRIVILEGES
- PostgreSQL documentation: pg_authid
- PostgreSQL downloads: Ubuntu
- MySQL 8.4 Reference Manual: mysqldump
- MySQL 8.4 Reference Manual: Command Options for Encrypted Connections
- MySQL 8.4 Server Error Message Reference