Skip to main content

Kubernetes Runner — Production Bot Deployment

The Kubernetes Runner deploys each bot as its own Deployment + Service + Ingress + PersistentVolumeClaim inside a Kubernetes cluster, giving you true per-bot isolation, horizontal scaling across nodes, and built-in observability via Prometheus. It's the right choice once you're running more than ~10 bots, want high availability, or already operate other workloads on Kubernetes.

This guide walks through cluster preparation, ingress and TLS configuration, kubeconfig setup, and how VolatiCloud actually deploys bots once everything is wired up. For the simpler single-host alternative, see Docker Runner.

When to Pick Kubernetes Over Docker

ScenarioRecommended runner
Fewer than 10 bots, personal useDocker — simpler
10–100 bots, team or solo at scaleKubernetes
100+ bots, production tradingKubernetes
High availability requiredKubernetes
Per-bot resource isolation neededKubernetes
Already running other workloads on K8sKubernetes

Prerequisites

  • Kubernetes 1.24+ cluster
  • kubectl access with cluster-admin permissions (needed for namespace, RBAC, and PVC creation)
  • Ingress controller installed (nginx-ingress recommended; any compatible controller works)
  • Optional but recommended: Prometheus Operator for metrics scraping
  • Optional but recommended: S3-compatible storage for SQLite database backups

Cluster Preparation

Install Nginx Ingress

If your cluster doesn't already have an ingress controller:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace

Create a Bot Namespace

VolatiCloud places each bot in a dedicated namespace (or a shard of namespaces at high scale — see Namespace Sharding below). Create the base namespace:

kubectl create namespace volaticloud-bots

Adding the Kubernetes Runner

VolatiCloud Runners list page showing existing runner rows with type and status columns, plus a green Add Runner button in the top right — the starting point for adding a Kubernetes runner

  1. Open RunnersCreate Runner
  2. Pick the Kubernetes runner type
  3. Fill in the configuration (described below)

Create Runner drawer with the Kubernetes runner type selected, revealing fields for kubeconfig, context, namespace, ingress host and class, TLS secret, storage class, shared data PVC, Freqtrade image, and Prometheus URL

Required Fields — Kubeconfig

FieldDescriptionExample
KubeconfigFull kubeconfig YAML, pasted(paste your kubeconfig)
ContextKubernetes context to usemy-cluster
NamespaceBase namespace for bot deploymentsvolaticloud-bots

The kubeconfig is encrypted at rest with field-level AES-256-GCM. Use a kubeconfig scoped to the bot namespace via RBAC if you don't want VolatiCloud to have cluster-admin in production — at minimum it needs Deployment, Service, Ingress, PersistentVolumeClaim, ConfigMap, and Secret permissions in the target namespace.

Required Fields — Ingress

FieldDescriptionExample
Ingress hostBase domain for bot URLs (each bot gets a subdomain)bots.company.com
Ingress classIngress controller classnginx
TLS enabledEnable HTTPS for bot ingresstrue
TLS secretKubernetes TLS secret namebots-tls

Required Fields — Storage

FieldDescriptionExample
Storage classKubernetes storage class for PVCsstandard
Shared data PVCPVC for shared historical OHLCV cachefreqtrade-data
Freqtrade imageDocker image for bot containersfreqtradeorg/freqtrade:stable

Optional — Observability

FieldDescriptionExample
Prometheus URLPrometheus server for metrics scrapinghttp://prometheus:9090

TLS Configuration

For HTTPS on bot ingress URLs, create a TLS secret in the bot namespace.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bots-tls
namespace: volaticloud-bots
spec:
secretName: bots-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- "*.bots.company.com"

Manual wildcard certificate

kubectl create secret tls bots-tls \
--cert=tls.crt \
--key=tls.key \
-n volaticloud-bots

Use a wildcard certificate (*.bots.company.com) so each bot gets its own subdomain without having to issue a certificate per bot.

Shared Data PVC

Create a single shared persistent volume for historical OHLCV data, mounted into every bot pod. This avoids re-caching the same OHLCV files per bot and dramatically reduces both startup time and storage cost.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: freqtrade-data
namespace: volaticloud-bots
spec:
accessModes:
- ReadWriteMany # required for sharing across pods
resources:
requests:
storage: 50Gi
storageClassName: standard

ReadWriteMany is required — confirm your storage class supports it (NFS, EFS, CephFS, Longhorn). If your storage class only supports ReadWriteOnce, you'll need a per-bot data volume instead, which is significantly more expensive at scale.

How Bots Are Deployed

When you start a bot on a Kubernetes runner, VolatiCloud:

  1. Picks a target namespace — the configured namespace, or one of N shards if namespace sharding is enabled
  2. Creates a Deployment with 1 replica running the configured Freqtrade image
  3. Creates a Service exposing the Freqtrade REST API
  4. Creates an Ingress so VolatiCloud's control plane can reach the API
  5. Creates a PersistentVolumeClaim for the bot's SQLite database (separate from the shared data PVC)
  6. Wires up Prometheus scraping if a Prometheus URL is configured
  7. Restores the SQLite database from S3 if S3 is configured and the bot was previously backed up

When you stop a bot, the database is uploaded to S3 (if configured), the pod is removed, and the PVC stays around for the next start. See ADR-0032 — Bot Database Backup Strategy for the backup contract.

Resource Allocation

Default resource requests and limits per bot:

ResourceRequestLimit
CPU100m500m
Memory256Mi512Mi

These are sized for typical 5–20 pair bots. Heavy strategies on many pairs may need higher limits — configure per-runner overrides in the runner's advanced settings.

S3 Database Backups

Configure S3 to automatically back up bot SQLite databases. The lifecycle:

Bot starts → SQLite restored from S3 (if exists) → bot runs → bot stops → SQLite uploaded to S3
Bot restarts → SQLite restored from S3 → trade history continues

This means trade history survives:

  • Pod restarts (e.g., node maintenance)
  • Runner migrations (rebuilding the cluster)
  • Accidental PVC deletion

Configure S3 endpoint, bucket, access key, secret, and region in the runner. Credentials are encrypted at rest the same as the kubeconfig.

Cluster Sizing Guidelines

BotsNodesRAM per nodeCPU per node
1–101–24 GB2 vCPU
10–503–58 GB4 vCPU
50–2005–1016 GB8 vCPU
200+10+32 GB16 vCPU

Pick node sizes that fit at least 4–8 bots per node so failure domains stay reasonable. Single huge nodes are an anti-pattern — they concentrate failure risk.

Namespace Sharding

For very high-scale deployments (100+ bots), VolatiCloud distributes bots across multiple namespaces. The reasoning:

  • Kubernetes RBAC lookups become slow with thousands of resources in a single namespace
  • ResourceQuotas and LimitRanges are per-namespace — sharding lets you set per-shard policies
  • Per-namespace controllers (network policies, OPA constraints) operate faster on smaller object sets
  • Operational blast radius is contained — a misconfigured policy on one namespace doesn't affect every bot

The shard count is configurable per runner. See ADR-0058 — Shared Ingress and Namespace Sharding for the architectural reasoning.

Test the Runner

After saving the runner, click Test Connection. VolatiCloud:

  • Connects to the cluster using the kubeconfig
  • Verifies namespace access
  • Checks the storage class is available
  • Confirms the ingress class exists
  • Reports any blocking issues with actionable error messages

Troubleshooting

Connection failed: forbidden

  • The kubeconfig's user lacks RBAC permissions in the target namespace. Grant Deployment / Service / Ingress / PVC / ConfigMap / Secret rights at minimum.

Storage class not found

  • Verify the storage class exists: kubectl get storageclass
  • Confirm it supports the access mode you need (ReadWriteMany for shared PVC, ReadWriteOnce for per-bot DB PVCs)

Ingress not provisioned

  • Confirm the ingress controller is installed and running
  • Verify the ingress class string matches your controller's class name

Bot pod stuck in Pending

  • kubectl describe pod to see the event log — usually insufficient resources or a missing PVC