Skip to content

Heroku-like PaaS CLI for vanilla Kubernetes. Zero server-side components.

License

Notifications You must be signed in to change notification settings

amanjain/kuberoku

kuberoku

Heroku-like DX on vanilla Kubernetes.
CLI + SDK. Zero server-side components. Open source.

CI PyPI Python License


You bring a Kubernetes cluster. Kuberoku gives you apps:create, deploy, config:set, services:logs --tail, and everything else you loved about Heroku — without vendor lock-in, without YAML, without installing anything on your cluster.

Install

pipx install kuberoku                                    # Python (recommended)
pip install kuberoku                                     # Python (alternative)
# Linux / macOS binary
curl -fsSL https://github.com/amanjain/kuberoku/releases/latest/download/install.sh | sh
# Windows (PowerShell)
irm https://github.com/amanjain/kuberoku/releases/latest/download/install.ps1 | iex

Pre-built binaries for Linux, macOS, and Windows are available on the GitHub Releases page.

Requires kubectl configured with a valid kubeconfig. The Python install requires Python 3.11+; the binary install has no Python dependency.

Quick start

Already have a cluster?

From zero to a running app accessible on the internet:

kuberoku clusters:doctor                                  # verify your cluster is ready
kuberoku apps:create myapi
kuberoku deploy --app myapi --image nginx:1.27 --expose web:80/http
# → https://myapi.apps.mycluster.com (auto-domain when base_domain is configured)
kuberoku config:set --app myapi GREETING=hello SECRET_KEY=abc123
kuberoku services:logs --app myapi --tail

That's it. No Deployments, Services, ConfigMaps, or Ingress manifests. Kuberoku created them all.

How ports work
Suffix Meaning What happens on deploy
/http HTTP service Auto-creates a domain via Ingress (myapp.apps.example.com) with TLS
/https Same as /http Alias — TLS is always via Ingress + cert-manager
/tcp Raw TCP ClusterIP Service; use services:expose:on for external IP
/udp Raw UDP ClusterIP Service; use services:expose:on for external IP

If you don't specify --expose, kuberoku defaults to a single web process on 8080/http.

Need a cluster first?

Local (fastest way to try):

We recommend k3d — it's the fastest to start and lightest on resources.

macOS
# k3d (recommended)
brew install k3d && k3d cluster create dev

# or kind
brew install kind && kind create cluster

# or minikube
brew install minikube && minikube start
Ubuntu / Debian
# k3d (recommended) — requires Docker
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
k3d cluster create dev

# or kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
kind create cluster

# or minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube start
Fedora / RHEL
# k3d (recommended) — requires Docker or Podman
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
k3d cluster create dev

# or kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
kind create cluster
Arch Linux
# k3d (recommended)
yay -S k3d-bin && k3d cluster create dev

# or kind
pacman -S kind && kind create cluster

Then run the commands above.

Cloud (DigitalOcean, Linode, AWS, GCP, Azure, etc.)

Three steps — same for every provider:

1. Download your kubeconfig

Every managed K8s provider gives you a kubeconfig file. Download it from your dashboard:

Provider Where to find it
DigitalOcean Kubernetes → your cluster → Download Config
Linode Kubernetes → your cluster → Download kubeconfig
AWS (EKS) aws eks update-kubeconfig --name my-cluster
GCP (GKE) gcloud container clusters get-credentials my-cluster
Azure (AKS) az aks get-credentials -g my-group -n my-cluster

AWS/GCP/Azure CLI commands auto-merge into ~/.kube/config. For DigitalOcean and Linode (or any provider that gives you a file), merge it yourself:

# First time — just copy it
mkdir -p ~/.kube
cp ~/Downloads/your-kubeconfig.yaml ~/.kube/config
# Already have a config? Merge the new one in (this MUST be one command):
KUBECONFIG=~/.kube/config:~/Downloads/new-kubeconfig.yaml kubectl config view --flatten > /tmp/merged && mv /tmp/merged ~/.kube/config

Common mistake: Running KUBECONFIG=... on its own line does nothing — it must be on the same line as the kubectl command.

2. Verify you can connect

kubectl get nodes

If you see your nodes listed, you're connected. If not, double-check your kubeconfig.

3. Run kuberoku

kuberoku clusters:setup          # checks your cluster and fixes what it can
kuberoku apps:create myapi
kuberoku deploy --app myapi --image nginx:1.27 --expose web:80/http

That's it. Kuberoku works on any conformant K8s cluster (1.33+). No special setup required.

Multiple clusters?

If you have more than one cluster (e.g. staging + production):

# See your available kubectl contexts
kubectl config get-contexts

# Register them in kuberoku
kuberoku clusters:add staging --context do-nyc1-staging --namespace kuberoku
kuberoku clusters:add production --context lke-prod --namespace kuberoku --default

# Switch between them
kuberoku clusters:switch staging
kuberoku clusters:switch production

What you get

Clusters and health checks

kuberoku clusters:add production --context prod-eks
kuberoku clusters:add staging --context staging-k3d
kuberoku clusters:switch production
kuberoku clusters:doctor                      # API, RBAC, CNI, Ingress, cert-manager, ...
kuberoku clusters:doctor --fix                # generate RBAC YAML for missing permissions
kuberoku clusters:setup --base-domain apps.myteam.dev   # configure auto-domains + DNS instructions

Doctor validates everything kuberoku needs: K8s API, namespace access, CRUD for all resource types, StorageClass, NetworkPolicy CNI, Ingress controller, cert-manager, LoadBalancer support, RBAC permissions, and base domain configuration.

Deploy and scale

# Simple: one process, default port 8080/http
kuberoku deploy --app myapi --image myapi:v2

# Multi-process: each --expose declares a process type and its ports
kuberoku deploy --app myapi --image myapi:v2 \
  --expose web:8080/http \
  --expose api:3000/http

# Push a local Docker image to the registry and deploy
kuberoku deploy --app myapi --image myapi:v2 --local

kuberoku ps:scale --app myapi web=3 worker=2        # scale independently
kuberoku ps:restart --app myapi                      # rolling restart
kuberoku ps:type --app myapi web --cpu 500m --memory 512Mi

Config and secrets

kuberoku config:set --app myapi DATABASE_URL=postgres://... API_KEY=secret
kuberoku config:set --app myapi --secret STRIPE_KEY=sk_live_...
kuberoku config --app myapi                   # secrets are masked
kuberoku config:unset --app myapi API_KEY

# Or set config inline during deploy (one atomic operation, one restart):
kuberoku deploy --app myapi --image myapi:v2 \
  --env DATABASE_URL=postgres://... \
  --secret-env STRIPE_KEY=sk_live_...

Config changes automatically trigger a rolling restart and create a new release.

Services, logs and exec

kuberoku services:logs --app myapi --tail          # stream logs
kuberoku services:logs --app myapi --type worker   # filter by process type
kuberoku services:exec --app myapi -- bash           # shell into a running pod
kuberoku services:connect --app myapi              # port-forward to localhost
kuberoku services:open --app myapi                 # open in browser

Expose a process to the internet:

kuberoku services:expose:on --app myapi web        # HTTP: auto-domain via Ingress
kuberoku services:expose:on --app myapi game \
  --method loadbalancer                            # TCP/UDP: external LoadBalancer IP
kuberoku services:expose:off --app myapi web       # revert to internal-only

Manage ports without redeploying:

kuberoku services:ports:add 9090/tcp --app myapi        # add a port
kuberoku services:ports:remove 9090 --app myapi         # remove a port

Domains and TLS

kuberoku domains:add myapi.example.com --app myapi                     # custom domain with auto-TLS
kuberoku domains:add api.example.com --app myapi --type api            # route to specific process
kuberoku domains:add internal.example.com --app myapi --no-tls         # HTTP only, no TLS
kuberoku domains --app myapi                                           # list all domains
kuberoku domains:remove myapi.example.com --app myapi
kuberoku domains:clear --app myapi                                     # remove all custom domains

When you deploy with /http ports, kuberoku auto-creates a domain (myapp.apps.example.com) with TLS via cert-manager. Custom domains added with domains:add also get auto-TLS by default.

Releases and rollbacks

kuberoku releases --app myapi                 # list all releases
kuberoku releases:info --app myapi 3          # inspect a specific release
kuberoku releases:rollback --app myapi        # roll back to previous
kuberoku releases:rollback --app myapi 2      # roll back to specific version
kuberoku releases:prune --app myapi --keep 10 # clean up old releases

Every deploy, config change, and rollback creates an immutable release. Full audit trail.

Built-in addons

kuberoku addons:create --app myapi postgres           # PostgreSQL 16
kuberoku addons:create --app myapi redis              # Redis 7
kuberoku addons:create --app myapi redis --as cache   # named instances
kuberoku addons:credentials --app myapi postgres      # show connection string
kuberoku addons:backup --app myapi postgres            # pg_dump backup
kuberoku addons:exec --app myapi postgres              # shell into addon pod

DATABASE_URL and REDIS_URL are injected automatically. Addons run as StatefulSets with persistent storage.

Maintenance mode
kuberoku apps:maintenance:on --app myapi      # scale all processes to 0, preserve replica counts
kuberoku apps:maintenance:off --app myapi     # restore previous replica counts

Deploy while in maintenance mode to update the image without running pods. When maintenance is turned off, the new image starts.

Non-HTTP services

Not everything is a web app. Deploy SMTP servers, game servers, DNS, gRPC — anything TCP/UDP:

kuberoku apps:create gameserver
kuberoku deploy --app gameserver --image game:v1 \
  --expose game:7777/udp,7778/tcp
kuberoku services:expose:on --app gameserver game --method loadbalancer
# External IP: 34.123.45.67  :7777/udp  :7778/tcp

/tcp and /udp ports get a ClusterIP by default — use services:expose:on for an external LoadBalancer IP. /http ports auto-create a domain via Ingress (no expose:on needed).

Preview environments

Spin up a full copy of your app for every pull request — from any CI provider:

kuberoku preview:deploy \
  --app myapi-pr-42 \
  --image myapi:pr-42 \
  --expose web:8080/http \
  --addon postgres \
  --env RAILS_ENV=staging \
  --ttl 48h \
  --output json

Each preview gets its own database, its own URL, and auto-destroys after the TTL expires:

kuberoku preview:list                         # see all active previews
kuberoku preview:status --app myapi-pr-42     # inspect one
kuberoku preview:destroy --app myapi-pr-42    # tear down manually
kuberoku preview:cleanup                      # destroy all expired

Set up your cluster's base domain for preview URLs:

kuberoku clusters:setup --base-domain apps.myteam.dev

This configures the base domain for auto-generated preview URLs and shows DNS setup instructions. Each preview app gets a URL like myapi-pr-42.apps.myteam.dev.

Shell completions

Tab-complete commands, options, and arguments:

eval "$(kuberoku completions bash)"           # bash — add to ~/.bashrc
eval "$(kuberoku completions zsh)"            # zsh — add to ~/.zshrc
kuberoku completions fish | source            # fish — add to config.fish

SDK

Every CLI command maps 1:1 to a Python method:

from kuberoku import Kuberoku

k = Kuberoku()

# Create and deploy
app = k.apps.create("myapi")
k.config.set("myapi", {"DATABASE_URL": "postgres://..."})
release = k.deploy.deploy("myapi", image="myapi:v2")  # defaults to web:8080/http

# Inspect
for app in k.apps.list():
    print(f"{app.name} v{app.release_version} ({'maintenance' if app.maintenance else 'active'})")

# Scale and rollback
k.ps.scale("myapi", {"web": 3})
k.releases.rollback("myapi")

All methods return frozen dataclasses. No K8s types leak into your code.

All commands

Group Commands
clusters add remove switch current info doctor setup registry:add registry:remove
apps create destroy info rename status link:add link:remove maintenance:on maintenance:off
completions bash zsh fish
deploy deploy (image or build-from-git, --env, --secret-env)
config set get unset
ps scale restart stop type set commands
services expose:on expose:off open connect ports:add ports:remove logs exec maintenance:on maintenance:off
domains add remove clear
releases info rollback prune
addons create destroy info scale migrate migrate-rollback credentials credentials-rotate exec backup expose:on expose:off connect
preview deploy destroy list cleanup status
plugins (list) install uninstall search

Run kuberoku <group> --help for details on any command.

How it works

Kuberoku stores all state in standard Kubernetes resources — ConfigMaps, Secrets, Deployments, Services, Ingress, NetworkPolicies. No CRDs, no operators, no server-side components. Uninstall kuberoku and your apps keep running.

You  ──>  kuberoku CLI (Click)  ──>  SDK (business logic)  ──>  K8s API
                                      |
                                 also importable as Python SDK

The SDK is a strict three-layer architecture. The CLI never touches K8s directly. A typing.Protocol abstracts the K8s client, making the entire stack testable with an in-memory fake.

See docs/NORTHSTAR.txt for the complete specification.

Project status

Alpha. Kuberoku is under active development. The core commands work and are well-tested, but the API may change between releases. Use it for development, staging, and side projects. Evaluate thoroughly before using in production.

If something breaks, please open an issue.

Security

If you discover a security vulnerability, please report it responsibly via GitHub's private security reporting instead of opening a public issue.

Contributing

Contributions welcome. Start with the NORTHSTAR spec to understand the architecture.

git clone https://github.com/amanjain/kuberoku.git
cd kuberoku
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v
mypy src/kuberoku/ --strict         # strict type checking
ruff check src/ tests/              # lint

License

Apache 2.0

Author

Built by Aman Kumar Jain.