VPS Snaps

Kubernetes

Velero if you have it. Manifests and CSI if you don’t.

Connect a cluster with a service account token and VPS Snaps picks the path when the run starts. Nothing is installed either way, and the run log records which one it took.

Back up your first server free, forever. No credit card required.

We only use your email to create your account. See our Privacy Policy.

How the data flows

Every run opens the same way and then splits. The test is a single cluster-scoped read: does the customresourcedefinition named backups.velero.io exist?

Authenticate, list the namespaces in scope, then ask one question

The connection is built from your API server URL, your CA certificate and the service account token. If you named namespaces on the job we use those; if you left the field empty we list every namespace the token can see. The answer to the Velero question is also written back onto the cluster record, so the badge you see in the dashboard is the same fact the worker acted on.

CRD found → we hand over

Drive the Velero you already run

Reimplementing Velero would be a worse Velero. It already exports resources and moves volume data through CSI or its own restic/kopia mover, into the storage location you configured when you installed it.

  1. We create a Backup custom resource in the namespace called velero, named vpssnaps-<job>-<timestamp>.
  2. Its ttl is written from your retention setting — 30 days becomes 720h0m0s — so Velero's own controller does the expiry rather than us running a second cleanup path.
  3. Every namespace in scope is listed explicitly in includedNamespaces.
  4. We poll the Backup's status every 20 seconds for up to 20 minutes. Completed passes. PartiallyFailed passes with the error count recorded as a warning. Failed and FailedValidation fail the run.

Where it lands: Velero’s own BackupStorageLocation. Not the storage destination on the job.

CRD absent → we do it ourselves

Export the manifests, attempt the volumes

No Velero, nothing to install, and a storage destination becomes required on the job — because now there is a file, and it has to go somewhere you own.

  1. Ten well-known resource kinds are listed per namespace through the Kubernetes API and serialised to YAML.
  2. A kind the token cannot read is skipped with a warning naming the kind, the namespace and the HTTP status — it does not sink the run.
  3. If volume snapshots are enabled, each PVC in the namespace is checked against the cluster's VolumeSnapshotClasses and snapshotted where there is a match.
  4. Everything is concatenated into one multi-document YAML file, MD5-hashed, and PUT into your bucket with the namespace and snapshot counts attached as object metadata.

Where it lands: Your S3-compatible bucket, under the job’s ID, as one .yaml object.

One file, and you can read it

The manifest path produces exactly one object per run: a multi-document YAML file with every namespace concatenated behind --- separators. Not an archive format, not a database, not anything that needs us to open it.

What ends up in your bucket
s3://acme-backups/k8s/<job-id>/
  k8s-backup-<job-id>-2026-09-03T02-00-11-402Z.yaml

  Content-Type:  application/yaml
  x-amz-meta-namespace-count:        4
  x-amz-meta-volume-snapshot-count:  2
  x-amz-meta-checksum-md5:           9f2c…

The MD5 is computed over the bytes that were uploaded and stored on the run as well as on the object, so you can verify later that what is in the bucket is what left the cluster. Two honest notes: the file is not compressed, and it is not redacted — see the FAQ on Secrets below before you decide where this bucket lives.

Ten kinds, per namespace

Config and secrets
ConfigMap, Secret
Workloads
Deployment, StatefulSet, DaemonSet
Batch
Job, CronJob
Networking
Service, Ingress
Storage
PersistentVolumeClaim

Enough to stand a typical application back up — its workloads, the config and secrets they read, and how they are exposed. Not enough to reconstitute a whole cluster, and we would rather say that than let you find out.

Volume snapshots, and the exact rule that decides

Kubernetes storage is not uniform, so this part is best-effort by design. The rule is small enough to state precisely.

For each PVC in the namespace we read its StorageClass, take that class’s provisioner, and look for a VolumeSnapshotClass in the cluster whose driver is that same string. A match means we create a VolumeSnapshot from the PVC and poll it every 20 seconds for up to 10 minutes until status.readyToUse comes back true.

No match means the PVC is skipped — not retried, not failed. Its manifest is still in the YAML, the loop moves to the next PVC, and the run goes on to succeed. What you get instead is a line in the log that names the thing that could not be done.

The warning you will actually see
warn  No CSI snapshot support for PVC prod/redis-data
      (StorageClass: standard) — skipping volume data,
      manifest still backed up

Snapshots you can find again

Each one is named vpssnaps-<job>-<pvc>-<epoch> and its namespaced reference is recorded on the run, so months later you can tie a snapshot in your cloud console back to the exact backup that produced it.

And snapshots we clean up after ourselves

A VolumeSnapshot lives in your cluster and your cloud account, where no bucket lifecycle rule will ever reach it. So after each successful run we go back through runs older than the job’s retention window and delete the snapshots we created — ours only, by name, never anything else in the namespace.

Or switch it off entirely

Volume snapshots are a toggle on the job, on by default. Turn it off and a run becomes a pure manifest export: fast, small, no snapshot charges, and no create permission needed on the service account.

A scoped service account and two commands

Read on the kinds we back up, plus create and delete on volumesnapshots if you want that half. Nothing wider, and nothing that can change a running workload.

The token and the CA certificate are both sealed with AES-256-GCM before they are written, and decrypted only inside the worker when a run begins. Test the connection before you build a job on it — the cluster list records what was tested, when it was tested, and whether Velero was found.

The full setup guide, with every flag

Create the service account and bind it
kubectl create serviceaccount vpssnaps-backup -n default

kubectl create clusterrole vpssnaps-backup-role \
  --verb=get,list,watch \
  --resource=namespaces,configmaps,secrets,services,\
persistentvolumeclaims,deployments,statefulsets,daemonsets,\
jobs,cronjobs,ingresses,storageclasses,volumesnapshotclasses

kubectl create clusterrolebinding vpssnaps-backup-binding \
  --clusterrole=vpssnaps-backup-role \
  --serviceaccount=default:vpssnaps-backup
Mint a token and pull the CA certificate
kubectl create token vpssnaps-backup -n default --duration=8760h

kubectl config view --raw \
  -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \
  | base64 -d

Volume snapshots need a second role carrying get,list,create,delete on volumesnapshots.snapshot.storage.k8s.io. Skip it and leave the toggle off if manifests are all you are after — everything else still works.

The bounds, stated plainly

Kubernetes backup is a field full of tools that imply more coverage than they deliver. Here is where ours stops, so you can decide what else you need beside it.

Well-known kinds, not full API discovery

Ten kinds per namespace, listed above. Custom resource definitions and the custom resources under them, RBAC objects, ServiceAccounts, HorizontalPodAutoscalers, NetworkPolicies, PersistentVolumes and the Namespace objects themselves are outside the set. Velero does full dynamic discovery; this deliberately does not, which is precisely why we hand over to Velero when it is present.

Velero has to be in the namespace called velero

That is Velero's own installer default and what nearly every chart and guide follows, and it is where we create the Backup resource. A Velero living in a namespace of its own choosing is the one configuration we cannot drive today.

On the Velero path, your storage destination is unused

The backup goes wherever Velero's configured BackupStorageLocation points, because that is Velero's decision, not ours. What we contribute is the schedule, the TTL taken from your retention setting, the polling, the run history and the alert if it fails.

Volume snapshots are crash-consistent, not quiesced

There are no pre- or post-hooks. We ask the CSI driver for a snapshot of the volume as it stands. For a database sitting on a PVC that is the equivalent of pulling the power — usually recoverable, never guaranteed. A dump taken by a database backup job is the stronger answer for that data.

Snapshots live in your cloud account and bill there

A VolumeSnapshot is an object in your cluster backed by your provider's snapshot service. We create ours, and we delete ours once they age past the retention window on the job. Everything in between is on your invoice, not on a plan limit here.

No CA certificate means no TLS verification

The field is optional because a handful of API servers present publicly trusted certificates. Most managed clusters do not. Leaving it blank still encrypts the connection, but nothing checks that the server on the other end is your cluster. Paste the CA.

The token is long-lived, and rotating it is on you

kubectl create token --duration=8760h gives you a year. Nothing here rotates it, and nothing warns you as the date approaches — when it lapses, runs begin failing on a 401 and you paste a fresh one into the cluster connection.

FAQs

Can’t find the answer you’re looking for? Reach out to our support team.

Do I have to install anything inside the cluster?

No. There is no operator, no DaemonSet, no sidecar, no Helm chart of ours. Everything happens over the Kubernetes API with a service account token you mint yourself and can revoke by deleting the ClusterRoleBinding. Velero is the one exception, and even there we install nothing — we look for its CRD and use the installation you already run.

What if my cluster has no CSI snapshot support at all?

You still get a complete manifest backup and a successful run. Each PVC without a matching VolumeSnapshotClass produces a warning line naming the PVC and its StorageClass, the loop continues to the next one, and the run finishes. The PVC object is in the YAML; its contents are not. Where that data matters, take it separately — a database job with a dump is more useful than a block snapshot anyway.

Which namespaces are backed up if I leave the field blank?

Every namespace the token is allowed to list, which on a normal cluster means kube-system, kube-public and whatever your ingress controller and cert-manager live in as well as your own. The field takes a comma-separated list — fill it in unless you have genuinely decided you want all of them, because manifest volume and run time both scale with it.

Does the manifest archive contain my Secrets?

Yes, in full. Kubernetes Secrets are base64 in the API, and base64 is an encoding rather than encryption — anything that can read the object can read the secret. The YAML sitting in your bucket is therefore as sensitive as the cluster it came from. Turn on encryption at rest for the bucket, keep the prefix's policy narrow, and treat the object accordingly.

Can I restore it with kubectl apply?

Not without a pass over the file first. Objects are dumped exactly as the API returned them, which means status blocks, uid, resourceVersion, creationTimestamp and cluster-assigned fields like a Service's clusterIP come along too. Split the file on its --- separators, strip those fields, then apply. It is plain YAML, so any tool you already use for that works, and none of it requires us.

My API server is not reachable from the internet.

Then this does not fit today, and it is better to say so. The Agent solves exactly that problem for database, Docker and file jobs — it holds an outbound WebSocket so nothing needs opening inbound — but it does not run Kubernetes jobs. A cluster backup connects from our worker to your API server, so the endpoint has to accept that connection, whether that is a public endpoint with the token scoped tightly or an allowlist you control.

Powerful features to give you peace of mind

Rest easy knowing your data and your reputation are safe.

Bring your own storage
Backups land in your own S3-compatible bucket — Backblaze B2, Wasabi, Cloudflare R2, or plain S3. You hold the keys and the data.
Snapshots stay with your provider
Provider snapshots are created through the provider's own API and never leave your account. We store the snapshot ID, not the image.
Seven providers, one dashboard
DigitalOcean, Hetzner, Vultr, Linode, AWS EC2, Google Compute Engine and Microsoft Azure — scheduled and reviewed from the same place.
Schedules that fit your traffic
Hourly, daily, weekly, monthly, or a fixed interval in minutes — anchored to your timezone, so a 02:00 job stays at 02:00 across a DST change.
Retention that prunes itself
Set how many days to keep. Older snapshots and archives are cleaned up after each successful run, so storage bills stay flat.
Step-by-step run logs
Every run records what it did, in order, with warnings and errors kept in place — so a failure tells you which step broke.
Complete run history
Status, duration, byte size, and what triggered each run, kept per job. Proof the backup ran, long after the night it ran.
Checksummed on upload
Every archive is hashed as it streams to your bucket and the checksum is stored with the run, so you can verify what landed.
Run on demand
Trigger any job by hand before a migration or a risky deploy, without touching its schedule or its retention window.
Alerts on five channels
Email plus Slack, Microsoft Teams, Google Chat, and Discord — on success, on failure, and on a job that missed its schedule entirely.
Encrypted credentials
SSH keys, database passwords, and storage secrets are sealed with AES-256-GCM before they touch the database.
Team access with roles
Invite your team into a shared workspace as owner, admin, or member, so backups outlive whoever set them up.
View all features
Chris Bennett
We manage a mix of VPS instances, Docker workloads, and traditional Linux servers, so backups used to be scattered across several different processes. VPS Snaps brought all of that into one dashboard and made it much easier to see what ran, what failed, and what needs attention.
Chris BennettSystems Administrator

Point it at a cluster and see which path it takes.

Connect with a token, run a backup by hand, and read the log. Whether it drove Velero or wrote the manifests itself, the run tells you exactly what it did.

No credit card required. Cancel anytime.