Tutorial 12 — Deploy and List on DigitalOcean (DOKS + Marketplace 1-Click)
> Status note.* The Helm chart at deploy/helm/buttrbase-backend-rust/ is real and committed — you can run it against a DOKS cluster right now using tutorial 07. The DigitalOcean Marketplace **Kubernetes 1-Click app** listing (the vendor portal submission, the DO review process) is forward-looking: this tutorial is a packaging guide for when you take that chart to market, not a guide to a listing that exists today. Steps that require the DO Marketplace vendor portal are marked *[Marketplace listing — forward-looking] so the distinction is never ambiguous.
This tutorial is one layer thin: it explains what DigitalOcean adds on top of the chart, how to push the image to DigitalOcean Container Registry (DOCR) so DOKS can pull it, how to wire DigitalOcean Managed Postgres into the chart's database-url secret, and what the 1-Click Kubernetes app packaging looks like. For everything about how Helm install actually works — topology values, secrets, the /health probe — see 07-deploy-with-helm.md. That document is the substrate; this one references it, it does not repeat it.
1. Why DigitalOcean
DigitalOcean is the lowest-friction path to a running ButtrBase install for self-serve SMB customers. The entire managed stack — DOKS, DOCR, and Managed Postgres — is available from a single account with predictable per-node pricing, no IAM scaffolding tax, and a control plane that a solo operator can manage without a dedicated infrastructure team.
This maps to the single-tenant-managed topology: ButtrBase operates the install on behalf of the customer, the customer gets a private, isolated instance, and the SMB customer never needs to know Kubernetes exists. It also suits the case where the customer themselves wants to run ButtrBase in their own DO account — the same chart and values file cover both.
| What DigitalOcean provides | What you still own |
|---------------------------|-------------------|
| DOKS managed Kubernetes (one command to get a cluster) | The Helm chart logic and values files |
| DOCR private container registry (same account, same billing) | The container image (ghcr.io/buttrbase/buttrbase-backend-rust) |
| Managed Postgres (connection URL ready to paste) | Mothership provisioning, encryption key management, support path |
| Marketplace 1-Click listing search and discovery | The upgrade path, SLA, and entitlement logic |
2. Prerequisites
- A DigitalOcean account with billing enabled.
doctlinstalled and authenticated:
``- Docker installed locally (to tag and push the image).
- Helm 3.12+.
- A DOKS cluster (1.25+). Create one via the DO control panel or
- Marketplace listing — forward-looking] A DigitalOcean Marketplace vendor account. Vendor enrollment is the gating step — DO reviews the application before you can submit a listing. See the [DigitalOcean Marketplace vendor documentation for the current process.
- Back to the substrate — if anything in the install behaves unexpectedly,
- AWS Marketplace — tutorial
- Azure Marketplace — tutorial
bash
doctl auth init
`
Follow the prompt to paste a personal access token from the DO control panel.
doctl kubernetes cluster create. Fetch its kubeconfig once it is ready:
`bash
doctl kubernetes cluster kubeconfig save
`
kubectl will now be pointed at the cluster.
3. Create a DigitalOcean Container Registry and push the image
DOCR lives in the same account as DOKS, which means the cluster can pull from it without additional image pull secret configuration (DO injects credentials automatically for registries in the same account).
Log in to the registry with
doctl:
doctl registry login
Tag the pinned release image and push it to your registry. Replace
with your DOCR registry name (visible in the DO control panel under Container Registry):
docker pull ghcr.io/buttrbase/buttrbase-backend-rust:0.1.0docker tag \
ghcr.io/buttrbase/buttrbase-backend-rust:0.1.0 \
registry.digitalocean.com//buttrbase-backend-rust:0.1.0
docker push registry.digitalocean.com//buttrbase-backend-rust:0.1.0
Do not list
latest — pin a real tag. The chart defaults to ghcr.io/buttrbase/buttrbase-backend-rust:latest; you will override the repository and tag with --set at install time.4. Provision DigitalOcean Managed Postgres and capture the connection URL
The chart requires an external Postgres database. DigitalOcean Managed Postgres is the natural backing store on DO: it is a fully managed cluster with automated failover, daily backups, and a trusted SSL connection string the chart can consume without additional CA certificate handling.
Create a cluster via the DO control panel (Databases → Create Database → PostgreSQL*), or use the
doctl CLI if you prefer it. Wait for the cluster to reach *Active status — this typically takes two to three minutes.Once active, the control panel shows a Connection Details* panel. Select *Connection string from the dropdown and copy the URI. It will look like:
postgresql://doadmin:@:/defaultdb?sslmode=require
Create a dedicated database and user for ButtrBase (the control panel provides a Users & Databases tab for this), then record the connection string for the new user and database. You will paste it into the Kubernetes Secret in the next step.
5. Create the namespace and Secret
The chart reads
database-url, secret-key, and encryption-key from a Kubernetes Secret you create — it never templates secrets into the ConfigMap. The secret must exist before helm upgrade --install runs.
kubectl create namespace buttrbasekubectl -n buttrbase create secret generic buttrbase-backend-rust \
--from-literal=database-url='postgresql://buttrbase:@:/buttrbase?sslmode=require' \
--from-literal=secret-key="$(openssl rand -hex 32)" \
--from-literal=encryption-key="$(openssl rand -hex 32)"
> The
encryption-key encrypts tenant secrets at rest. Losing it means losing access to every encrypted value — store it in your secret manager before you install, not after. See docs/encryption-at-rest.md.6. Install on DOKS
Dry-run first to confirm the topology values rendered correctly:
helm template buttrbase ./deploy/helm/buttrbase-backend-rust \
-f ./deploy/helm/buttrbase-backend-rust/values-single-tenant-managed.yaml \
--set app.image.repository=registry.digitalocean.com//buttrbase-backend-rust \
--set app.image.tag=0.1.0
Confirm the rendered
ConfigMap carries BUTTRBASE_RUST_DEPLOYMENT_MODE: "single-tenant-managed" and the correct BUTTRBASE_RUST_STORAGE_BACKEND. Then install:
helm upgrade --install buttrbase ./deploy/helm/buttrbase-backend-rust \
-n buttrbase \
-f ./deploy/helm/buttrbase-backend-rust/values-single-tenant-managed.yaml \
--set app.image.repository=registry.digitalocean.com//buttrbase-backend-rust \
--set app.image.tag=0.1.0
upgrade --install is idempotent — the first run installs, every subsequent run is a rolling upgrade. The values-single-tenant-managed.yaml file sets topology.mode: single-tenant-managed, networkMode: private, and providers.ownership: customer-managed, which are the correct defaults for a DO-hosted managed install. Adjust topology.tenantSlug, topology.publicBaseUrl, and policy.ipAllowlistCidrs for the specific tenant.7. Package as a DigitalOcean Kubernetes 1-Click Marketplace app
[Marketplace listing — forward-looking]
DigitalOcean's Kubernetes Marketplace apps are Helm charts packaged according to the marketplace-kubernetes vendor guidelines. The chart at
deploy/helm/buttrbase-backend-rust/ is the artifact — you wrap it, you do not rewrite it.The packaging requirements are lighter than GCP's deployer image model. A Kubernetes 1-Click app on DO is essentially:
| Artifact | Purpose |
|----------|---------|
| The Helm chart itself | Packaged as a
.tgz (standard helm package) or pointed at a Helm repo URL |
| deploy.sh (optional helper) | A shell script DO's 1-Click runner may invoke for pre-flight checks; many listings omit it |
| Listing metadata | Name, description, logo, category, support URL — entered in the vendor portal |
| A working demo cluster URL | DO validates the chart deploys cleanly to a test DOKS cluster before approving the listing |The vendor portal submission is web-only. In the portal you upload or link the chart, fill in metadata, and point DO at the chart's values file for the defaults a customer sees on first install. DO then runs an automated test deploy against an internal DOKS cluster. Once the automated check passes, a DO reviewer approves the listing manually. Expect several business days for a first submission.
The chart's
values-single-tenant-managed.yaml is the natural default values file for the 1-Click listing: it gives the customer a private, isolated install with sane defaults, and the topology fields (tenantSlug, publicBaseUrl, storageBucket) are the only fields a customer needs to fill in through the listing UI.Reference the marketplace-kubernetes repository and the Kubernetes app submission guidelines for the exact artifact format and validation checklist.
8. Verify
Verification is identical to tutorial
07 — the chart is the same chart. Check readiness and probe the health endpoint:
kubectl -n buttrbase rollout status deploy/buttrbase --timeout=120s
kubectl -n buttrbase port-forward svc/buttrbase 4300:4300 &
curl -fsS http://localhost:4300/health && echo " ✓ healthy"
Confirm the topology round-tripped:
kubectl -n buttrbase exec deploy/buttrbase -- printenv BUTTRBASE_RUST_DEPLOYMENT_MODE
→ single-tenant-managed
If the pod is not ready, read the logs — the two dominant first-install failures are a
database-url the pod's network cannot reach (DOKS must be in the same DO region and VPC as the Managed Postgres cluster, or use the public endpoint with sslmode=require) and an encryption-key that is not 32 bytes hex. Both appear in the first ten log lines:
kubectl -n buttrbase logs deploy/buttrbase --tail=50
Done
You have the full DigitalOcean path: the image is in DOCR, Managed Postgres is wired into the Secret, the chart is installed on DOKS, and the deployment mode is verified end-to-end. From here:
07-deploy-with-helm.md is the ground truth for chart behavior, values semantics, and secret layout.
09 covers the same chart packaged as an EKS Marketplace listing.
10` covers the Managed Application and AKS path.