Skip to content

Backup Storage#

Plan required

Backups are available only on the Premium and Enterprise plans. Freemium accounts cannot back up, restore or schedule backups.

Volume backups for a service location are executed by the Ametnes agent and stored in a backup repository that you provide. This page explains how to choose and configure that repository, distribute credentials, and operate the backup jobs.

The control plane stores the backup ledger and schedules. It never reads or writes the repository and never stores repository or cloud credentials. The agent creates standard Kubernetes Jobs in the resource's namespace; no CRDs or operators are installed.

For the conceptual overview (manual runs, schedules, retention, and deletion), see Backups. For console workflows, see Backup Operations.

Before you begin#

  • A running agent installed with Helm in the location cluster.
  • A repository that every node can reach at the same path (mount target) or an S3-compatible bucket (S3 target).
  • The repository password stored in a Kubernetes Secret, or a password file in the mounted repository.

Backups are disabled by default (agent.config.backup.enabled: false). While disabled, agents do not process INIT runs. DELETING runs are always processed so resource deletion can clean up snapshots.

Choose a target#

Target Use when Trade-offs
mount You already run NFS or another shared filesystem and want the repository on storage you control. The same export must be mounted at the same path on every node. The repository directory is opaque; copy it by snapshot, not rsync.
s3 You want the repository reachable from every node without filesystem setup. Requires S3 credentials (or workload identity) and a repository password Secret in every service namespace.

The default repository path for a mount target is <mount_path>/ametnes/<location>, so one repository holds every resource's snapshots for that location.

Mount target#

agent:
  config:
    backup:
      enabled: true
      target:
        type: mount
        password_secret:
          name: backup-repository-password
          key: RESTIC_PASSWORD
        mount:
          host_path: /var/lib/ametnes-backup

Expose the repository in one of two ways:

Key Meaning
mount.host_path A path on each node, for example an NFS export mounted at /var/lib/ametnes-backup. The Job can schedule on any node, so the same export must be mounted at the same path on every node.
mount.host_path_type Host path type for mount.host_path. Defaults to Directory; leave it at Directory (see the warning below).
mount.claim_name A PVC in the service namespace. Pods cannot mount a PVC from another namespace. Use an RWX NFS PVC so Jobs on any node can mount it.
mount.mount_path Path where the repository appears inside the backup Job. Defaults to /repo.
mount.repository Explicit repository path. Defaults to <mount_path>/ametnes/<location>.

Keep the host path type as Directory. With DirectoryOrCreate, a missing mount silently becomes an empty local directory and the agent initialises a wrong, node-local repository.

When using mount.claim_name, a PV binds to exactly one PVC. Sharing one export across namespaces therefore means one static PV/PVC pair per namespace, or a per-namespace subdirectory created by an NFS provisioner.

For NFS, mount with NFSv4.1 or later, hard, noatime, a low actimeo, _netdev, and never nolock. Export with sync, and keep clocks synchronised (NTP) on nodes and the NFS server.

The repository directory is opaque. To take a copy, snapshot the export (or stop writes) and copy the snapshot. Never rsync a live repository during a backup or maintenance. A copy of the repository directory is directly usable and is the portable, customer-owned artifact.

S3 target#

agent:
  config:
    backup:
      enabled: true
      target:
        type: s3
        password_secret:
          name: backup-repository-password
          key: RESTIC_PASSWORD
        s3:
          endpoint: https://s3.eu-west-2.amazonaws.com
          bucket: ametnes-backups
          prefix: prod-euw2
          region: eu-west-2
          secret_name: backup-s3-credentials
Key Meaning
s3.endpoint S3-compatible endpoint.
s3.bucket Bucket that holds the repository.
s3.prefix Key prefix inside the bucket.
s3.region Region for the S3 client.
s3.secret_name Secret containing the access key and secret key. Leave empty to use workload identity.
s3.access_key_key Key for the access key ID. Defaults to AWS_ACCESS_KEY_ID.
s3.secret_key_key Key for the secret access key. Defaults to AWS_SECRET_ACCESS_KEY.

prefix is not location-scoped. When several locations share an endpoint and bucket, set a distinct prefix per location. Otherwise they share one repository.

An S3 target requires target.password_secret. target.mount.password_file is mount-only and is rejected for S3 because an S3 Job has no repository mount from which to read the file.

Repository password#

Setting Target Meaning
target.password_secret mount and s3 Secret in each service namespace containing the repository password. Required for S3, optional for mount.
target.mount.password_file mount only Path inside the mounted repository, for example /repo/.backup-password. Takes precedence over the Secret when both are set.

Password settings belong at the target level, not inside mount or s3. The agent rejects mount.password_secret and s3.password_secret and tells you to move them. target.password_file is accepted as a deprecated alias for mount targets and rejected for S3.

Distribute credentials#

Backup Jobs run in the service namespace, and a Secret cannot be referenced across namespaces. Keep the source Secrets in the agent namespace and let the agent copy them:

kubectl -n ametnes-system create secret generic backup-repository-password \
  --from-literal=RESTIC_PASSWORD='<repository password>'

kubectl -n ametnes-system label secret backup-repository-password \
  ametnes.io/distribute=scheduled

kubectl -n ametnes-system create secret generic backup-s3-credentials \
  --from-literal=AWS_ACCESS_KEY_ID='<key>' \
  --from-literal=AWS_SECRET_ACCESS_KEY='<secret>'

kubectl -n ametnes-system label secret backup-s3-credentials \
  ametnes.io/distribute=scheduled

The marker can be a label (preferred) or an annotation:

Value Behaviour
scheduled Create the copy in every agent-managed namespace and keep it in sync. Updating the source rotates the copies.
once Create the copy where missing. Never update or delete it.
true Alias for scheduled.

The agent reconciles shared Secrets every five minutes and immediately after a resource namespace is created. Managed namespaces are ametnes-<account>-<project> and account-<account>. Copies carry ametnes.io/distributed=true and ametnes.io/distributed-from. A scheduled copy is deleted when its source Secret is deleted or loses the marker.

TLS certificate and CA copies written by the certificate and CA tasks also carry ametnes.io/distributed=true, but with ametnes.io/distributed-mode=control-plane, so this reconciler never rewrites or deletes them.

Then point the target at the copies:

agent:
  config:
    backup:
      target:
        password_secret:
          name: backup-repository-password
          key: RESTIC_PASSWORD
        s3:
          secret_name: backup-s3-credentials

The Secrets are readable by principals that can read Secrets in the tenant namespace, like the TLS Secrets already distributed there. For stronger tenant isolation, use a repository per tenant with tenant-owned credentials.

Configuration reference#

Agent chart values under agent.config.backup:

Key Default Meaning
enabled false Process INIT runs. DELETING runs are always processed.
timeout 3600 Job timeout and lease duration base, in seconds.
retention 30 Fallback retention window, in days, when a run is not created by a schedule.
scale_wait 10 Seconds to wait after scaling down before capturing volumes.
poll_interval 5 Job status polling interval, in seconds.
max_concurrent 4 Concurrent per-volume Jobs.
target.* - Repository target described above.

Control plane (control/config/<profile>/config.yml):

Key Default Meaning
backup.stale_timeout 21600 A run stuck in INIT, or a deletion stuck in DELETING, is marked ERROR after this many seconds.

On startup the agent logs the effective backup configuration: target type, repository, S3 region, credential sources (Secret names or workload identity), timeout, retention, and concurrency. Secret values are never logged.

Concurrency and locking#

  • A Kubernetes Lease per resource prevents two operations on the same resource across nodes and agent replicas.
  • A repository Lease serializes repository maintenance across agents writing the same repository. It lives in the agent namespace.
  • The backup engine holds repository locks. Repository maintenance waits for an interrupted backup to finish before it continues.

Backups of different resources may run concurrently on different nodes. Only repository maintenance is exclusive per repository.

Operations#

# Backups in flight
kubectl get jobs -A -l app.kubernetes.io/managed-by=ametnes-agent

# A specific run's Jobs
kubectl get pods -n <service-namespace> -l ametnes.io/backup-run=<run-id>

Use your backup tooling to inspect the snapshot inventory for a resource or to list repository locks.

Common failures:

Symptom Cause Action
Job Pending, persistentvolumeclaim ... not found Manifest references a missing PVC. Upgrade the agent; maintenance Jobs no longer mount data volumes.
CreateContainerConfigError, secret ... not found Password Secret configured but absent. Create the Secret, or (mount targets only) use mount.password_file.
repository is already locked A killed Job left a repository lock. Wait for restic to treat the lock as stale (30 minutes by default), or clear the stale lock with your backup tooling in a controlled window.
Retention Job fails while backups succeed Concurrent maintenance or lock conflict. Current versions serialize repository maintenance with the repository lease; check the lease and agent logs.
Run stuck in INIT The claiming agent disappeared. The control plane marks it ERROR after backup.stale_timeout.

Limitations#

Product-level limits (quiesced-only backups, same-resource restore, deleting a record vs. its snapshots) are covered in Backups. Operator-facing limits are:

  • Cross-location restore and disaster recovery are not implemented. A shared repository across locations is not serialized by the repository lease.
  • There is no repository garbage collector. Orphaned snapshots are removed by the resource deletion flow or by manual repository maintenance.
  • Snapshots written before tenant-qualified tags were introduced cannot be matched by the current filters and are not removed automatically.
  • Ledger retention is not functional; backup records are not aged out automatically.
  • Deleted-resource tombstones can appear in listings for up to one reaper interval while snapshots are removed.