Backup destinations
Configure and verify storage for backups.
A backup destination is a reusable storage target — an S3-compatible bucket, an SFTP server, a local
filesystem path, or another Openship server — that backup policies write their archives
to. This API creates and manages those targets and runs a preflight that proves a destination is
actually reachable (it writes, reads back, and deletes a probe object). Credentials are encrypted at rest;
serialized destinations never return secret values, only hasX flags showing which are configured.
This module is available on both self-hosted and cloud instances. The local destination kind is
self-hosted only — it is rejected in cloud mode and, on self-hosted, must be explicitly enabled (see below).
Create a destination
const result = await ship.backupDestinations.create({
name: "prod-s3",
kind: "s3_compatible",
bucket: "my-backups",
region: "us-east-1",
accessKeyId: "AKIA...",
secretAccessKey: "...",
});
console.log(result);curl -X POST "$OPENSHIP_URL/api/backup-destinations" \
-H "Authorization: Bearer $OPENSHIP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"prod-s3","kind":"s3_compatible","bucket":"my-backups","region":"us-east-1","accessKeyId":"AKIA...","secretAccessKey":"..."}'Prop
Type
Required fields depend on kind:
s3_compatible—bucket,accessKeyId, andsecretAccessKey.sftp—sshHost,sshUser, and one ofsftpPassword/sftpPrivateKey.openship_server—serverId(a server in your org).local—endpoint(an absolute path insideBACKUP_LOCAL_ROOT).
Update a destination
Send only the fields you want to change. For credentials, undefined (omitted) leaves the stored value
unchanged, null clears it, and a string replaces it. The kind cannot be changed after creation.
const result = await ship.backupDestinations.update("bkd_123", {
pathPrefix: "nightly/",
isDefault: true,
});
console.log(result);curl -X PATCH "$OPENSHIP_URL/api/backup-destinations/bkd_123" \
-H "Authorization: Bearer $OPENSHIP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pathPrefix":"nightly/","isDefault":true}'Prop
Type
Preflight (verify connectivity)
Proves the destination works by writing, reading back, and deleting a small probe object. On success it
stamps lastVerifiedAt; on failure it records the error in lastVerifyError.
const result = await ship.backupDestinations.preflight("bkd_123");
console.log(result);curl -X POST "$OPENSHIP_URL/api/backup-destinations/bkd_123/preflight" \
-H "Authorization: Bearer $OPENSHIP_TOKEN"A failed check is still a 200
Preflight returns 200 with {"ok": false, "reason": "..."} when the target is unreachable or
misconfigured — the reason (bad credentials, DNS failure, permission denied) is in the body, not the HTTP
status. A 404 here means the :id doesn't belong to your organization.
Errors
400 — validation failed
The body is missing a field required for its kind (e.g. an S3 destination without a bucket or
credentials), the name is blank / over 80 chars or duplicates an existing destination, or you passed an
unsupported kind. http_upload is defined but not yet supported.
400 — local destinations disabled
A local destination requires BACKUP_ALLOW_LOCAL_DESTINATION=true and a configured BACKUP_LOCAL_ROOT,
and the endpoint must resolve to an absolute path inside that root. Both the root and the endpoint are
symlink-resolved before the check, so a symlink that escapes the root is rejected. BACKUP_LOCAL_ROOT
itself must be a dedicated directory — not /, /var, /var/lib, Openship's own state directory, or
anything inside a system tree such as /etc or /var/lib/docker. Local destinations are always rejected
in cloud mode.
404 on a per-destination route
The :id doesn't belong to your organization (or was deleted). List your destinations with
GET /api/backup-destinations to get valid IDs.
Operations
| Operation | SDK | REST API |
|---|---|---|
| List backup storage destinations. | backupDestinations.list() | GET /api/backup-destinationsbackup_destination:list |
| Create a backup destination. | backupDestinations.create(input) | POST /api/backup-destinationsbackup_destination:write |
| Test destination configuration before saving it. | backupDestinations.preflightDraft(input) | POST /api/backup-destinations/preflightbackup_destination:write |
| List backup history across storage destinations. | backupDestinations.history(input?) | GET /api/backup-destinations/historybackup_destination:list |
| List backup runs stored in one destination. | backupDestinations.runs(id, input?) | GET /api/backup-destinations/:id/runsbackup_destination:read |
| Read a destination with credentials protected. | backupDestinations.get(id) | GET /api/backup-destinations/:idbackup_destination:read |
| Read destination storage usage. | backupDestinations.usage(id) | GET /api/backup-destinations/:id/usagebackup_destination:read |
| Update destination settings. | backupDestinations.update(id, input) | PATCH /api/backup-destinations/:idbackup_destination:write |
| Remove an eligible destination. | backupDestinations.remove(id) | DELETE /api/backup-destinations/:idbackup_destination:admin |
| Test a saved destination and record its verification state. | backupDestinations.preflight(id) | POST /api/backup-destinations/:id/preflightbackup_destination:write |