• Get started

    • Getting started
    • Concepts
    • Tour
  • Using shpyrd

    • Deploying
    • shpyrd.yaml
    • Resources
    • Databases and caches
    • Domains and exposure
    • Sign-in for your app
    • Teams, roles and security
    • AI assistants (MCP)
    • Logs
    • Dashboard
    • CLI reference
  • Running it yourself

    • Installation
    • Oracle Cloud (OKE)
    • AWS (EKS)
    • Extensions and sign-in
    • Platform backups
    • Architecture guide
  • Project

    • Design principles
    • Roadmap
    • How to contribute
    • Support the project
  1. Running it yourself
  2. Installation
Add toClaudeClaude CodeClaudeClaudeOpenAICodexCursorCursorVisual Studio CodeVS Code

Installation

Create a local cluster with the shpyrd base stack, or install it on an existing Kubernetes cluster.

shpyrd is open source; this page is for running it on your own cluster. On shpyrd cloud the platform is run for you: see Getting started. The single CLI, shpyrd, installs the platform on a Kubernetes cluster: a local kind cluster it creates for you, or a cluster you already have - on Oracle Cloud (OKE) or AWS (EKS), other providers as their profiles arrive. This page covers the local cluster and what every profile shares.

Requirements

  • Docker (Docker Desktop on macOS/Windows, Docker Engine on Linux). Give it 6-8 GB of memory: the base stack idles around 3 GB and buildpack builds need headroom.
  • Internet access for the first install (kind node image, Helm charts, buildpacks, ~2 GB) and for name resolution of the default 127.0.0.1.nip.io domain.
  • macOS or Linux, Intel or ARM. On Windows, use WSL 2.
Docker Desktop and cgroup v1

Recent Kubernetes releases refuse to run on cgroup v1. If Docker Desktop has the deprecated cgroup v1 setting enabled (DeprecatedCgroupv1 in its settings), shpyrd cluster create detects it, applies a kubelet override and warns; switching Docker Desktop to cgroup v2 is recommended.

Install the CLI

macOS, with Homebrew:

brew install shpyrd-io/tap/shpyrd

macOS or Linux, with the install script (downloads the latest release, verifies its SHA-256 checksum and installs into /usr/local/bin or ~/.local/bin):

curl -fsSL https://shpyrd.io/install.sh | sh

SHPYRD_VERSION=v0.1.0 pins a version and SHPYRD_INSTALL_DIR=... picks the directory. The archives and checksums are also on the release page for a manual install. Check with shpyrd version.

Every release also publishes the server image ghcr.io/shpyrd-io/shpyrd-server:<version> for linux/amd64 and linux/arm64; the CLI installs the image of its own version, so CLI and server always match. Upgrading is brew upgrade shpyrd (or re-running the script) followed by shpyrd cluster init.

Building from source

Developers build the CLI with make cli (Go 1.27) after cloning shpyrd-io/shpyrd; a development build installs the latest released server image unless told otherwise with --set SHPYRD_SERVER_IMAGE=... (see the contributing guide).

Create a local cluster

shpyrd cluster create

This runs kind as a library to create a two-node cluster named shpyrd (one control-plane, one worker), then installs the base stack in dependency-ordered runlevels, waiting for each to be healthy:

LevelComponents
rc0Prometheus Operator CRDs
rc1cert-manager, the registry credential
rc2development CA ClusterIssuer, trust-manager, ingress-nginx (host ports 80/443), the in-cluster registry (TLS from the CA) and the node trust for it
rc3kpack with the Paketo buildpacks builder, kube-prometheus-stack + Grafana, the control-plane database (PostgreSQL)
rc4shpyrd server (API, App controller, dashboard)

The first run takes 10-20 minutes, mostly downloads. Re-running cluster create or cluster init on an existing cluster is idempotent and takes about 30 seconds.

Options worth knowing:

shpyrd cluster create --http-port 8080 --https-port 8443   # host ports 80/443 already in useshpyrd cluster create --domain myapps.example.test          # wildcard domain resolving to your machineshpyrd cluster create --workers 2                          # more kind worker nodesshpyrd cluster create --skip monitoring                    # lighter install, no Prometheus/Grafanashpyrd cluster create --no-init                            # only the kind cluster

Local names and ports

Two choices decide what your URLs look like, and shpyrd cluster create detects the fitting ones:

DefaultAlternative
Names*.127.0.0.1.nip.io: public DNS answers 127.0.0.1, nothing to install (needs internet for name resolution)--local-dns with a domain such as shpyrd.test: a dnsmasq rule and a /etc/resolver/test file make every *.test name resolve to your machine, offline (macOS; one sudo prompt; dnsmasq installed with Homebrew if missing)
Front doorkind maps 80/443 to ingress-nginx; certificates from shpyrd's development CA (cluster trust-ca)--front-door caddy when a Caddy already serves 443: kind takes high ports, shpyrd writes ~/.shpyrd/caddy/shpyrd.caddy proxying *.<domain> to kind and reloads Caddy; certificates come from Caddy's CA you already trust, URLs carry no port

When Caddy is detected the CLI asks:

Caddy v2.11.4 is serving port(s) 80 and 443 on this machine.Use it as the front door (clean https URLs, certificates from Caddy's CA)? [Y/n]

and, unless --domain was given, uses shpyrd.test (offering to configure dnsmasq when the name does not resolve yet). The Caddyfile needs one line, import ~/.shpyrd/caddy/*.caddy, which the CLI offers to add to the Caddyfile of the running Caddy. The result:

  Dashboard:  https://shpyrd.shpyrd.test  Names: dnsmasq (*.shpyrd.test) · Front door: Caddy on 443 -> kind :8080

--yes accepts the proposals (CI does this); --front-door kind refuses them. Both choices are recorded in the cluster, so shpyrd cluster init, cluster status and cluster dashboard keep them; cluster destroy removes the Caddy site and offers to remove the DNS rule if shpyrd wrote it. An existing cluster can switch: shpyrd cluster init --domain shpyrd.test --front-door caddy (identity providers then need the new redirect URIs).

Trust the platform CA

Not needed behind a Caddy front door: Caddy issues the certificates from its own CA (run caddy trust once if your browser warns). Otherwise, certificates for https://<project>.<domain> are issued by a root CA generated on your machine (~/.shpyrd/ca/rootCA.pem) and stored in the cluster. Install it in your operating system trust store once:

shpyrd cluster trust-ca      # asks for sudo (macOS keychain / Linux ca-certificates)

Firefox keeps its own store: enable security.enterprise_roots.enabled in about:config or import the certificate.

Cloud clusters generate their own platform CA at install; it signs the in-cluster registry and the platform's internal endpoints, never the public hostnames (those come from Let's Encrypt). shpyrd cluster trust-ca --context <cluster> fetches and installs that CA when you need to talk to the registry from your machine.

Check the installation

shpyrd cluster status
Profile: local  Version: v0.1.1  Domain: 127.0.0.1.nip.io  Updated: 2026-09-21T22:23:28ZNames: public DNS (127.0.0.1.nip.io) · Front door: kind on 80/443
RUNLEVEL  COMPONENT        STATUS  VERSION  APPLIEDrc0       monitoring-crds  ready   32.0.0   ...rc1       cert-manager     ready   v1.21.2  ...rc2       ca-issuers       ready            ...rc2       trust-manager    ready   v0.25.0  ...rc2       ingress-nginx    ready   4.15.1   ...rc2       registry         ready            ...rc3       kpack            ready            ...rc3       monitoring       ready   91.4.1   ...rc3       control-plane-db ready            ...rc4       shpyrd           ready            ...

Endpoints on the default domain:

  • https://shpyrd.127.0.0.1.nip.io — dashboard
  • https://grafana.127.0.0.1.nip.io — Grafana (admin / shpyrd on the local profile)
  • 10.96.0.50:5000 — the in-cluster registry, inside the cluster only (shpyrd cluster registry shows its state)

Sign in

The installer generated one credential, the admin token, stored as a Secret in the cluster. The CLI uses it through your kubeconfig without you noticing; the dashboard asks for it:

shpyrd cluster dashboard     # opens the dashboard signed in, through a one-time ticketshpyrd cluster token         # prints the token for the "Admin token" field of the sign-in page

For one developer on a laptop that is all. For a team, enable accounts and make yourself the first platform admin - from the first team on, roles are enforced:

shpyrd cluster init --enable auth-localshpyrd users add you@example.com --name "You"                                       # prompts for a passwordshpyrd teams create platform --platform-role platform-admin --member you@example.com

Then sign in with the email and password, and switch the token off when nobody needs it (shpyrd cluster token --disable; --rotate replaces it). Details: Extensions and sign-in, Teams, roles and security.

Deploy the example

The repository bundles an example, a Go module with a web and a worker process:

shpyrd projects create hello-worldcd examples/hello && shpyrd deployshpyrd open                              # https://hello-world.127.0.0.1.nip.ioshpyrd secrets set GREETING="Olá mundo"  # new release, the page picks it upshpyrd scale web=3 worker=2shpyrd logs -f
Ports 80 and 443 taken?

shpyrd cluster create --http-port 8080 --https-port 8443 maps other host ports; URLs then carry the port (https://hello-world.127.0.0.1.nip.io:8443).

Install on an existing cluster

The installer works against any kubeconfig context:

shpyrd cluster init --context my-cluster --profile local --domain apps.example.test --yes

--yes is required for contexts that do not look like kind clusters. The local profile assumes ingress-nginx can bind host ports on a node labelled ingress-ready=true and that the service subnet is 10.96.0.0/16 (the registry uses the fixed ClusterIP 10.96.0.50). For a managed Kubernetes cluster use a cloud profile: Oracle Cloud (OKE) or AWS (EKS).

Upgrading

Install the new CLI and run shpyrd cluster init again with the same context and profile: the recorded decisions (domain, exposure, extensions) are reused, and --only shpyrd limits the run to the platform's own components when nothing else changed. What an upgrade does to running apps:

  • The apps keep serving throughout: the platform's server restarts, the apps do not depend on it at run time.
  • A release that changes what every instance is given (a new platform variable such as REVISION, a new resource model) rolls every app's instances once, one at a time; a single-instance app is unavailable for the seconds its new instance takes to start.
  • A release that changes what builds are made of (the buildpacks, the stack, the run image) makes kpack rebuild every buildpack app; the previous release keeps serving until the new image is ready, and a failed rebuild leaves it serving and marks the project so. v0.9.11 moved every image to a repository named after its workspace, which rebuilt every buildpack app once; the old repositories stay in the registry until a prune exists.
  • A release with a database migration (v0.9.11: identifiers became native uuid; v0.9.13: the memberships and invitations tables) migrates at the server's first start; take a backup first (shpyrd cluster backup, or pg_dump against the control-plane-db pod). Backups made by v0.9.13 carry workspace roles (dump version 2) and cannot be restored by an older server.
  • v0.9.13 adds workspace roles. Nothing changes for existing people: a team's platformRole still counts for anyone without a workspace role. Give yourself the owner role (shpyrd people role you@example.com owner) so the workspace has one; a workspace role, once set, decides over the team's.
  • Re-applying every component (without --only) restarts ingress-nginx, which is a real interruption of a few seconds at the front door.

Environment profiles

A profile describes the environment the base stack is built for and therefore how load balancing, DNS, TLS and the registry are provided:

localoci (Oracle Cloud)aws (AWS)
Load balancerkind host ports 80/443, or your CaddyOCI flexible load balancer on a reserved address; a private one for internal projectsNetwork Load Balancers with pod targets (AWS Load Balancer Controller): an internet-facing one on Elastic IPs, an internal one for internal projects
DNS*.127.0.0.1.nip.io or dnsmasq (*.shpyrd.test)a wildcard record you create, or a zone in OCI DNS managed by ExternalDNSa zone in Route 53 managed by ExternalDNS (alias records)
TLSdevelopment CA issued by cert-managerLet's Encrypt (one wildcard with a DNS provider); the platform CA for the registryLet's Encrypt (one wildcard through the Route 53 solver); the platform CA for the registry
Registryin-cluster, TLS from the CAin-cluster, TLS from the CA; OCIR with --registry-hostin-cluster, TLS from the CA
Isolationkindnet enforces NetworkPolicyCalico in policy-only modethe VPC CNI's network policy agent
Storagekind's local pathBlock Volume (50 GB minimum), File Storage for shared volumesEBS gp3, EFS for shared volumes
Accessthis machineWireGuard instance (profile from Terraform), or the Bastion tunnelAWS Client VPN (profile from Terraform)

The dashboard's cluster page shows the installed profile, and the install record keeps every choice so cluster init re-runs need no flags.

Export the manifests

For GitOps tooling (Flux, Argo CD) the same embedded manifests can be rendered to disk instead of applied:

shpyrd cluster export -o ./gitops --domain apps.example.test

Remove everything

shpyrd cluster destroy     # deletes the kind cluster

Projects, images and configuration live inside the cluster and disappear with it; the development CA under ~/.shpyrd/ca is kept so the next cluster is trusted immediately.

On this page

  • Requirements
  • Install the CLI
  • Create a local cluster
  • Local names and ports
  • Trust the platform CA
  • Check the installation
  • Sign in
  • Deploy the example
  • Install on an existing cluster
  • Upgrading
  • Environment profiles
  • Export the manifests
  • Remove everything
  • Docs

shpyrd is open source under MPL-2.0, and in beta.