Database Backups¶
You can back up your databases managed by Appliku through the dashboard. Backups can be stored on the server or uploaded to S3-compatible storage.
Cluster vs. Standalone Backups¶
Backups for standalone datastores run a one-shot container on the chosen backup server, attached to the local applications Docker network. The backup server must be the same server that hosts the datastore when the connection mode is private_connection_url.
Backups for cluster-deployed datastores run a one-shot container attached to the cluster's proxy overlay network, so they can run on any active, set-up server in the same cluster — not necessarily the DB-pinned node. The backup server selector is validated to enforce this. Local backup artifacts and S3 uploads still happen on whichever server runs the cron job, so changing the backup server reschedules the cron on the new server and removes the stale cron entry from the previous one.
How to Back Up a PostgreSQL Database¶
- Open the Appliku dashboard
- Go to your application that has a database
- Click on the "Manage" link in the "Databases" card:

- Pick your database
- Click on the "Add backup" link:

- Specify parameters for the backup:


Available schedule intervals range from every 30 minutes to daily. For object storage, pick one of the supported providers and supply the credentials for it: most settings (including the endpoint URL) are filled in for you.
Object Storage Providers¶
When you choose Object Storage (S3-compatible) as the backup target, pick one of the providers below and follow the matching setup section. The form fills in as much as it can automatically: the endpoint URL is derived from the region or the account ID for every preset, so you only need to supply the bucket and the credentials.
The sections that follow walk through creating the bucket and the credentials with each provider, then map the values onto the fields shown in the Appliku backup form.
Amazon S3¶
- Create an AWS account at aws.amazon.com if you don't have one.
- In the AWS Console, open S3 and click Create bucket. Pick a unique bucket name and a region. Block all public access unless you have a specific reason not to. Keep the region handy.
- Open IAM, go to Users, click Create user, and give it programmatic access.
- Attach an inline policy that allows
s3:PutObject,s3:GetObject,s3:DeleteObject, ands3:ListBucketonarn:aws:s3:::your-bucketandarn:aws:s3:::your-bucket/*. Avoid using a full-access policy. - After creating the user, open it and create an Access key. Copy the access key ID and secret access key once; AWS does not show the secret again.
Then fill in the backup form:
| Field | Value |
|---|---|
| Storage Provider | Amazon S3 |
| Bucket | The S3 bucket name |
| Region | The AWS region you created the bucket in (e.g. us-east-1) |
| Access Key ID | The IAM user's access key ID |
| Secret Access Key | The IAM user's secret access key |
| Path Prefix | Optional folder inside the bucket (e.g. appliku-backups) |
The endpoint is selected automatically from the region.
DigitalOcean Spaces¶
- Sign in to DigitalOcean and open Spaces Object Storage.
- Click Create a Spaces Bucket, pick a datacenter region (e.g.
nyc3,fra1,sfo3), choose a unique Space name, and enable Restrict File Listing. - Open API in the left sidebar and scroll to Spaces Keys. Click Generate New Key, give it a name, and copy the access key and secret. The secret is shown only once.
Then fill in the backup form:
| Field | Value |
|---|---|
| Storage Provider | DigitalOcean Spaces |
| Space Name | The Space name you created |
| Region | The datacenter slug (e.g. nyc3, fra1, sfo3) |
| Spaces Access Key | The Spaces access key |
| Spaces Secret Key | The Spaces secret key |
| Path Prefix | Optional folder inside the Space |
The endpoint resolves to https://{region}.digitaloceanspaces.com automatically.
Cloudflare R2¶
- In the Cloudflare dashboard, open R2 Object Storage. If R2 is not yet enabled on the account, accept the terms; R2 has a generous free tier and you can use it without entering payment details until you exceed it.
- Click Create bucket, pick a unique name and a location hint, and create the bucket.
- Open Manage R2 API Tokens (top right on the R2 overview page). Click Create API Token, choose Object Read & Write, scope it to the bucket you just created, and create the token.
- On the token page, copy the Access Key ID and Secret Access Key. Cloudflare also shows your Account ID in the right sidebar of the R2 page; copy that too.
Then fill in the backup form:
| Field | Value |
|---|---|
| Storage Provider | Cloudflare R2 |
| Bucket | The R2 bucket name |
| Cloudflare Account ID | The account ID from the R2 overview page |
| R2 Access Key ID | From the R2 API Token |
| R2 Secret Access Key | From the R2 API Token |
| Path Prefix | Optional folder inside the bucket |
The endpoint resolves to https://{account_id}.r2.cloudflarestorage.com automatically. R2 does not use regions; Appliku signs requests with auto for you.
Wasabi¶
- Sign up at wasabi.com and confirm the account.
- In the Wasabi console, open Buckets and click Create Bucket. Pick a unique name and a region from the dropdown. Wasabi region codes match Appliku's options (e.g.
us-east-1,eu-central-1,ap-northeast-1). - Open Access Keys, click Create New Access Key, choose Root User (or a user you created under Users & Groups with bucket permissions), and download the keys. The secret is shown only at creation.
Then fill in the backup form:
| Field | Value |
|---|---|
| Storage Provider | Wasabi |
| Bucket | The Wasabi bucket name |
| Region | The Wasabi region of the bucket |
| Access Key ID | The Wasabi access key |
| Secret Access Key | The Wasabi secret key |
| Path Prefix | Optional folder inside the bucket |
The endpoint is selected from Wasabi's per-region service URLs automatically. Some Wasabi regions have non-standard endpoints (notably us-east-1 uses s3.wasabisys.com with no region in the hostname), so always use the dropdown rather than typing a region by hand.
Custom S3-compatible¶
The Custom S3-compatible option works with any service that exposes the AWS S3 API, including self-hosted MinIO, SeaweedFS, Garage, Backblaze B2 (S3 API), Hetzner Object Storage, OVH Cloud Object Storage, Scaleway Object Storage, and any other compatible provider.
Each service has its own console, but the steps are always the same:
- Create a bucket in the provider's console.
- Generate an S3-compatible access key and secret key with read/write/list/delete permissions on that bucket.
- Look up the endpoint URL the provider exposes for the S3 API; this is the value the AWS CLI would pass as
--endpoint-url. Examples:https://s3.eu-central-1.example.com,https://gateway.storjshare.io, or for a self-hosted MinIO on a private network something likehttp://minio.internal:9000.
Then fill in the backup form:
| Field | Value | Notes |
|---|---|---|
| Storage Provider | Custom S3-compatible | |
| Bucket | The bucket name | |
| Region | Optional region label | Set this if your provider requires a region in the signature (e.g. us-east-1). Leave empty otherwise. |
| Endpoint URL | The provider's S3 endpoint | Must include the scheme. Both http:// and https:// are accepted; use http:// only for private networks. A trailing slash is stripped automatically, so https://endpoint.example.com/ and https://endpoint.example.com are equivalent. |
| Access Key ID | The S3-compatible access key | |
| Secret Access Key | The S3-compatible secret key | |
| Path Prefix | Optional folder inside the bucket | Leading and trailing slashes are added automatically; backups, /backups, backups/, and /backups/ all save as /backups/. |
| Addressing Style | Auto, Virtual-hosted, or Path |
Most managed services accept Auto. Pick Path if your provider requires path-style URLs (https://endpoint/bucket/key rather than https://bucket.endpoint/key); self-hosted MinIO and SeaweedFS typically need this. |
If you are unsure whether Auto, Virtual-hosted, or Path is right, run Test Storage with Auto first; if it fails with a DNS or NoSuchBucket error, switch to Path and try again.
AWS CLI Version¶
Uploads run through the official amazon/aws-cli Docker image on your server. Appliku pins a verified version of that image by default, because new AWS CLI releases have repeatedly broken uploads to S3-compatible providers (for example, the checksum defaults introduced in 2.23.0 and a crash on error responses with an empty message — both hit non-AWS services such as Hetzner Object Storage, MinIO, and Ceph).
The AWS CLI Version field on the backup form lets you override the image tag per backup:
| Value | Behavior |
|---|---|
| (empty) | Use the platform default (a pinned, verified version) |
latest |
Always docker pull the newest image before each backup — you get new releases immediately, including any regressions |
A specific tag, e.g. 2.36.24 |
Use exactly that version |
Appliku also sets AWS_REQUEST_CHECKSUM_CALCULATION=when_required and AWS_RESPONSE_CHECKSUM_VALIDATION=when_required for the upload, so newer CLI versions do not send checksums that many S3-compatible providers reject.
Note
The Test Storage button uses the same AWS CLI version the backup job will use, so a passing test is meaningful for the actual upload.
Test Storage Before Saving¶
Each remote backup form has a Test Storage button that uploads a tiny test file from the selected server and deletes it on success. The button uses the same Docker image and credentials as the scheduled backup, so a passing test confirms the backup will be able to write to the destination. The test must pass before you can create a new remote backup, and the form will ask for a fresh test if you change storage settings on an existing backup.
Local Storage Retention¶
When using Local Filesystem as the backup target, you can set Keep Last N Backups to automatically delete old backup files and keep only the most recent N copies. For example, setting it to 7 keeps the last 7 backup files and removes older ones.

Leave the field empty to keep all backups with no automatic deletion.
Retention is applied automatically every 6 hours by Appliku.
For Object Storage (S3) targets, the backup file is removed from the server immediately after a successful upload. When an upload fails, the file stays on the server's disk — those leftovers are visible on the backup detail page under Files on Server Disk. The Delete Leftover Files After (days) setting on the backup form deletes them automatically (a file is matched after that many full days have passed). New backup tasks default to 30 days; backup tasks created before this feature have automatic deletion disabled and keep their files until you set a value. Leave the field empty to keep files forever.
Every backup run also verifies that the backup directory is writable before doing any work, so permission problems fail the run loudly instead of leaving undeletable files behind.
- After submitting the form you will be taken back to the database page with your new backup task:

Run History¶
Every backup run records one entry — start time, end time, success or failure, uploaded file size, and a short error description on failure. Appliku collects these records from your server about once an hour and stores the last 100 runs per backup task.
You see them in two places:
- The backups table shows a Last Run status for each task.
- The backup detail page has a Run History table with every collected run.
Note
Run history covers runs the backup script actually reported. A run appears in the history up to an hour after it happened, when the next collection cycle picks it up.
If a scheduled backup starts while the previous run of the same task is still in progress, the new run is skipped and a note is written to the backup log. Skipped starts do not appear in the run history.
Backup Problem Alerts¶
Appliku flags a backup task as problematic in two cases:
- Failing — the latest reported run of the task failed.
- Stale — the task has not reported a run when its schedule says it should have (for example, cron is not running on the server). A task is considered stale after two missed scheduled runs plus a one-hour grace period, computed in the server's local timezone — and only after Appliku has successfully checked the server for run reports past that point, so a temporarily unreachable server does not cause a false alarm.
A flagged task shows a red warning triangle in the backups table. When a task starts failing, the team receives a notification; when a run succeeds again, the team receives a recovery notification. In-between runs send nothing — you get one message per state change, not one per failed run. Every Monday, teams with flagged tasks also receive a digest email listing them.
Backup notifications use the same Slack/Discord toggles as server notifications, plus each member's personal notification preferences. You can turn all backup notifications off for a team with the Notify about failing or overdue database backups switch in the team notification settings; the warning triangle stays visible regardless.
View Backup Logs and Files¶
Open a backup task from the backups table to inspect its latest runtime information.
The backup detail page shows the last 200 lines from the backup job log on the server:
For Local Filesystem backups, the page also lists the latest compressed backup files from:
The file list shows server paths, file sizes, and modification times. S3 backups show the configured bucket and path instead of local server files.
Backup Command Details¶
The pg_dump command used to perform backups looks like this:
What these arguments do:
--no-privileges— Excludes permissions/privileges from the dump file--no-owner— Excludes ownership information from the dump file--no-acl— Excludes access control lists from the dump file
Backup to File¶
To store a PostgreSQL backup into a compressed file:
Backup from Docker Container¶
Since PostgreSQL runs in a Docker container, the full command looks like this:
docker run --rm --network applications -e DATABASE_URL=<postgres_url> \
-v /home/app/_backups/{backup_id}:/backup postgres:15 sh -c \
"pg_dump --no-privileges --no-owner --no-acl \"$DATABASE_URL\" | gzip > /backup/<export_filename>"
What each part does:
docker run --rm— Starts a temporary container that is removed after it exits--network applications— Connects the backup container to the same Docker network as the database-e DATABASE_URL=...— Passes the selected database connection URL into the backup container-v /home/app/_backups/{backup_id}:/backup— Mounts the backup directory from the host into the containerpostgres:15— Uses the official PostgreSQL 15 Docker image- The shell command runs
pg_dump, pipes throughgzip, and saves to the mounted backup directory
Backup Connection Modes¶
Backups can connect to PostgreSQL in three modes:
- Connection URL uses the public database URL. This mode is available only when external connections are enabled for the database.
- Private Connection URL uses the internal Docker network URL. The backup task must run on the same server as the database.
- Custom lets you provide a PostgreSQL URL manually. If external connections are disabled for the database, the backup task still must run on the database server.
For databases with external connections disabled, new backup tasks default to Private Connection URL and run on the database server.
Restore from Backup¶
To restore a PostgreSQL database from a backup file:
- Download the backup file
- Uncompress it:
- Import to the database using
psql: