Getting started
60 seconds
You need an account with billing set up and kubectl mode enabled. Enabling kubectl mode is an owner action and requires TOTP; see kubectl mode if the button is not there yet.
Download your kubeconfig from https://app.dollarbox.dev/kubectl/<your-org-slug>/kubeconfig/. It saves as dollarbox-<your-org-slug>.kubeconfig.
export KUBECONFIG=./dollarbox-your-org.kubeconfig
kubectl get pods
Expected on a fresh org:
No resources found in org-2f1c8a04-... namespace.
That output means the token authenticated, the namespace exists, and your context is already scoped to it. You never pass -n. If you get error: You must be logged in to the server (Unauthorized), the credential was revoked or replaced — download it again.
Prefer not to write manifests? Provision a container does the same thing from the control panel.
IPv6
This is the failure mode that catches most people. DollarBox workloads are reachable over IPv6 only. There is no public IPv4 address for your service.
Your kubeconfig points at the cluster API over IPv4, so kubectl itself works from an IPv4-only network. IPv6 is what you need to reach the service you deploy.
Test your machine:
curl -6 https://test-ipv6.com/
Content back means you have working IPv6. Nothing back means you do not, and the failure you will hit later looks like this:
$ curl -6 https://app.example.com/
curl: (7) Failed to connect to app.example.com port 443: Network is unreachable
Note the shape of it: DNS resolves fine and dig AAAA app.example.com returns your address. Only the connection fails. This is not a DNS problem and not a DollarBox problem.
On an IPv4-only network:
- Try mobile data. Most mobile networks are IPv6.
- Check whether IPv6 is switched off on your home router. It often is by default.
- Use a public NAT64/DNS64 resolver for one-off testing.
- Use a tunnel broker if you need stable IPv6 from a fixed IPv4-only network.
For production traffic from IPv4 clients, see Reaching your service from IPv4 clients below.
What you actually get
One Kubernetes namespace per org, named org-<uuid>. You cannot create namespaces yourself; extra child namespaces (org-<uuid>-<slug>) are provisioned through the control panel or API and carve a slice out of the same paid capacity.
Quota
Quota scales with the number of dollarboxes you subscribe to. For N subscribed dollarboxes at €1/month each excluding VAT, the org-quota ResourceQuota is:
| Quota key | Limit |
|---|---|
pods | N + 1 |
count/pods | N + 5 |
requests.memory / limits.memory | N Gi |
requests.cpu | N × 100m |
limits.cpu | N × 1000m |
requests.ephemeral-storage / limits.ephemeral-storage | N × 5 Gi |
requests.storage | N × 10 Gi, plus your pre-paid volume pool |
persistentvolumeclaims | N × 2, plus your pre-paid volume count |
services.loadbalancers | N |
count/services | N × 5 |
count/secrets | N × 10, minimum 5 |
count/configmaps | N × 10 |
count/deployments.apps | N × 2, minimum 10 |
count/replicasets.apps | 11 × the deployment limit |
count/events | N × 200, minimum 500 |
An org-limits LimitRange fills in what you leave out: default request 100m CPU / 256Mi memory / 256Mi ephemeral storage, default limit 1000m CPU / 1Gi memory / 2Gi ephemeral storage, and a hard per-container ceiling of 1000m CPU / 1Gi memory / 5Gi ephemeral storage. A single container cannot exceed one core, one gigabyte of memory or five gigabytes of node-local scratch no matter how many dollarboxes you buy — buy more to run more pods, not bigger ones. Ephemeral storage is the writable image layer, emptyDir volumes and logs; a container that writes past its limit is evicted on its own, so put anything larger on a volume.
Separately from quota, your account has a plan cap. The default tier allows 5 containers and 50 GB of storage; raising it is a quota-increase request in the control panel.
Already in your namespace
org-quotaResourceQuota andorg-limitsLimitRange.- Four NetworkPolicies: default-deny ingress, allow-same-namespace ingress, allow-same-namespace egress, and an egress policy that blocks other tenants' pod, service and node ranges while leaving the public internet open.
container-reserved-ipv6— a read-only ConfigMap listing the IPv6 addresses reserved for your org, comma-separated under theaddresseskey.- The
tenant-usersRoleBinding your credential is a subject of.
Everything labelled dollarbox.io/managed-by=control-panel is read-only from kubectl, and the container- name prefix is reserved.
What your credential can do
| Resources | Verbs |
|---|---|
| pods, pods/log, pods/exec, pods/portforward | get, list, watch, create, update, patch, delete |
| services, configmaps, persistentvolumeclaims, events | get, list, watch, create, update, patch, delete |
| deployments, deployments/scale, replicasets | get, list, watch, create, update, patch, delete |
| secrets | create, update, patch, delete — no get, list or watch |
You can write a Secret and mount it. You cannot read one back through the API. Deleting one works by name. Keep the source of truth somewhere else.
The credential is namespace-wide, not per-user: anyone with a kubeconfig for your org can exec into any pod in it and consume shared quota.
What you cannot create
Not in your RBAC at all: Ingress, StatefulSet, Job, CronJob, DaemonSet, NetworkPolicy, VolumeSnapshot, ServiceAccount, Role, RoleBinding, Namespace, PersistentVolume, HPA.
Rejected by admission control even though the resource type is allowed:
- Services that are not
ClusterIPorLoadBalancer.NodePortandExternalNameare out. LoadBalancerServices that are not IPv6SingleStack, that setexternalIPsorspec.loadBalancerIP, or that request an address outside your reservation.- Pods or pod templates using host networking,
hostPID,hostIPC,nodeSelector,nodeName, node affinity, tolerations beyond the two not-ready/unreachable ones,priorityClassName, or a non-default scheduler. - PersistentVolumeClaims on any storage class other than
local-pathorlonghorn, or with any access mode other thanReadWriteOnce. - Anything named
container-*, and anything carrying thedollarbox.io/managed-bylabel.
Pods run under PodSecurity restricted. Set runAsNonRoot, drop all capabilities, disable privilege escalation, and use the RuntimeDefault seccomp profile, or the pod is rejected.
Deploy something real
There is no Ingress on DollarBox. Tenants have no Ingress permissions and the platform terminates no TLS for you. A public service is a Deployment plus an IPv6 LoadBalancer Service on one of your reserved addresses, with TLS handled inside your pod or at a proxy you put in front. That is the whole model.
1. Find a reserved address
kubectl get configmap container-reserved-ipv6 -o jsonpath='{.data.addresses}'
Each subscribed dollarbox reserves one address. They are sticky — yours across restarts, deletes, recreates and node failover — and a LoadBalancer Service that does not name one of them is rejected. The same list is on the kubectl page in the control panel.
2. Apply the workload
Save as web.yaml, replacing the annotation value with one of your reserved addresses. This uses traefik/whoami, which runs unprivileged on a high port and is what DollarBox's own smoke test deploys.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: whoami
image: traefik/whoami:v1.10
args: ["--port", "8080"]
ports:
- containerPort: 8080
securityContext:
runAsUser: 65532
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
requests:
memory: 32Mi
cpu: 10m
limits:
memory: 64Mi
cpu: 100m
---
apiVersion: v1
kind: Service
metadata:
name: web
annotations:
metallb.io/loadBalancerIPs: <one-of-your-reserved-addresses>
spec:
type: LoadBalancer
ipFamilies: [IPv6]
ipFamilyPolicy: SingleStack
selector:
app: web
ports:
- port: 80
targetPort: 8080
protocol: TCP
kubectl apply -f web.yaml
Prove the pod is up:
kubectl rollout status deployment/web --timeout=120s
deployment "web" successfully rolled out. If it hangs, kubectl get pods and kubectl describe pod -l app=web — a restricted violation shows up there as a rejected create, and an over-quota create shows up as a FailedCreate event on the ReplicaSet.
3. Prove the address is assigned
kubectl get svc web -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
That prints the reserved address you asked for. Ingress rules for the port you exposed are reconciled by the platform within a few minutes; there is no cluster-admin step.
4. Prove it serves traffic
From a machine with IPv6:
curl -6 http://[2a01:4f8:fff0:18b:1000::1]:80/
Substitute your own address. Brackets are required.
5. Put a hostname on it
One AAAA record, pointing at that address. No CNAME, no flattening.
dig AAAA app.example.com
curl -6 http://app.example.com/
Full walkthrough: Point a hostname at your dollarbox.
6. TLS
DollarBox does not terminate TLS. Terminate it in your own container or in a reverse proxy you run in your namespace, on port 443 behind a second LoadBalancer port. For certificates, use DNS-01 with Let's Encrypt — HTTP-01 validation is unreliable over IPv6-only paths.
Reaching your service from IPv4 clients
Two directions, and they are not symmetric.
Outbound — your pod calling IPv4-only services. Covered by the cluster NAT64 gateway, with no configuration on your side. Docker Hub, npm, GitHub and ordinary public APIs work from your pods as they would anywhere else. You do not configure NAT64 inside the container.
Inbound — IPv4 clients reaching your service. Not covered. The NAT64 gateway is outbound-only: it translates connections your pod opens, and nothing else. An IPv4-only client cannot open a connection to your IPv6 address, and DollarBox has no shared IPv4 address or IPv4 proxy offering — not on the €1 plan, not as an add-on. If your audience includes IPv4-only networks and you cannot put a proxy in front yourself, DollarBox is the wrong host and we would rather you knew now.
To serve IPv4 clients you bring your own edge: a proxy that speaks IPv4 to the client and IPv6 to your pod. Cloudflare's proxy does this for HTTP and HTTPS on your own Cloudflare account — a proxied AAAA record, Full (strict) TLS mode, and a certificate on your origin. It does not forward arbitrary TCP or UDP; non-web protocols need Cloudflare Spectrum or direct IPv6.
Limits and suspension
Two different things get a namespace stopped.
Not paying. A failed invoice moves the org to past due immediately and schedules suspension one day later. On suspension, control-panel containers are scaled to zero and every kubectl credential for the org is revoked — your kubeconfig stops working, and workloads you created with it keep running but you cannot manage them. Paying the invoice resumes containers and you download a new kubeconfig. Nothing is deleted at this stage.
Breaking the Terms. The acceptable-use list is short and we mean it literally:
- No illegal content under Irish or EU law.
- No spam, no phishing.
- No cryptocurrency mining.
- No denial-of-service attacks.
- No deliberately exhausting shared cluster resources.
Violations end the account immediately and without a refund. Abuse reports go to [email protected].
Beyond that: there is no uptime SLA, at any price. Outages are posted to status.dollarbox.dev. If our infrastructure breaks we fix it. If your container breaks, that is yours.
Backups
The €1 base price includes no backups. That is deliberate, not an oversight. Nothing in your namespace is backed up unless you pay for the add-on and switch it on per volume.
Two things worth being precise about first:
- The ephemeral pod disk is gone on every restart, image change and recreate.
- The
local-pathstorage class writes to one node's local disk and is permanently lost when that node is drained, upgraded or fails. It is for scratch data only. Anything you care about belongs onlonghorn.
The paid add-on
€0.15 per backed-up GB per month excluding VAT, prorated, charged on the source volume's size — a 10 GB volume is €1.50/month regardless of how many backup copies are retained. This is on top of the €0.10/GB/month you already pay for the volume itself. The control panel shows the exact cost before you enable it.
What is backed up: the contents of the individual longhorn PersistentVolumeClaims you enable it on. Nothing else, and nothing by default.
What is not backed up, ever:
- The ephemeral pod disk.
local-pathvolumes.- Any
longhornvolume you did not explicitly enable backups on. - Secrets and ConfigMaps.
- Your Deployments, Services and other manifests — no cluster state is captured.
- Namespace configuration, quota, reserved addresses, or your container images.
A restore gives you back a volume's bytes. It does not give you back your application.
Frequency: one backup a night, starting at 02:00 UTC plus a per-volume offset of up to two hours to spread load. Plus one manual "back up now" on demand — one live manual backup at a time; delete the old one to take another.
Retention: configurable from 1 to 30 days, meaning that many successful nightly backups are kept. Older ones are pruned automatically.
Where they are stored: on Hetzner Storage Boxes in the same region — separate hardware from the dedicated servers whose disks hold your live Longhorn volume, so losing the cluster does not lose the backups. Transfers are incremental after the first full copy.
Backup data is not encrypted at rest, and neither are your volumes. DollarBox does not encrypt either one. Access to the backup store is restricted to the platform's backup infrastructure, and that is the whole of the protection. If your data needs encryption at rest, encrypt it in your application before it is written to the volume.
How to enable it: per volume, at Volumes → Backups in the control panel, or through the API:
PUT /api/v1/volumes/<volume-id>/backup-policy/
PUT /api/v1/namespaces/<namespace-id>/volumes/<pvc-name>/backup-policy/
You cannot enable backups from kubectl. Tenant credentials have no VolumeSnapshot permissions; the platform takes the snapshots on your behalf.
How to restore: self-service, from the same screen or POST to /api/v1/volumes/<volume-id>/backups/<backup-id>/restore/. You do not open a ticket and you do not wait for us.
A restore always creates a new volume. In-place restore does not exist, by design — your original volume and its data are never touched. The new volume comes out of your pre-paid pool at the backup's original size, so the pool needs that much free space, and restores cannot resize. Once it is bound, attach it like any other volume.
A restore starts the moment you ask for it — it is not waiting on a nightly job or a periodic sweep. How long it then takes scales with how much data the backup holds, because the volume is rebuilt by pulling it back from backup storage. Watch the restore move pending → provisioning → bound rather than assuming a duration. There is no guaranteed turnaround, and the Terms do not offer one.
Billing lifecycle: past-due and suspended accounts keep their existing backups and keep being charged for them, but take no new ones and prune nothing until the account is active again. Closing an account deletes all of its backups. Disabling backups on a volume deletes every retained backup for it, and the charge stops once deletion finishes.
What you should still do yourself
Even with the add-on on:
- Keep your own off-site copy of anything you cannot lose. One vendor, one region.
- Take application-level dumps. These are block-level copies of a running volume, taken without quiescing your application. A database restored from one recovers the way it would from a power cut.
pg_dumpinto object storage you control is not redundant with this. - Keep your manifests in git. Backups do not contain them.
- Store Secrets somewhere else. They are not backed up and you cannot read them back out of the API.
- Test a restore before you need one. Restore to a new volume, mount it, read the data. A backup you have never restored is a hypothesis.
Full detail: Volume backups. For same-cluster recovery points at €0.10/GB/month, see snapshot protection — those are not off-site and do not survive loss of the cluster.
Refer a developer
Once you are running, there is a referral link on Dashboard → Team & referrals → Referrals. Every developer who signs up through it, stays 30 days and pays €2 of usage earns you €1 of credit — a container-month — on your next invoice. They pay normal prices. Details and the rules are in Referrals.
Getting help
Support is the docs and a community channel. At €1/month there is no support team, and best-effort means what it says.
- Technical questions — the community Discord. The invite is in your signup confirmation email and on your dashboard; it is not published on this site.
- Billing — [email protected].
- Abuse reports — [email protected].
- Security vulnerabilities — [email protected]. See the Security Policy.
Restores need no support channel — they are self-service in the control panel and over the API, at any hour. If a restore itself fails, the restore record carries the error; take that to the Discord like any other technical problem. Do not send it to the billing address, which is slower and not monitored for technical issues.