Skip to Content
PyoDocs

System reference

The PYO_ environment variables, and everything Pyo runs on your server.

Most of Pyo is configured from the dashboard. This page covers what sits underneath it, for when you run or troubleshoot the installation yourself.

Change a setting

Pyo reads its environment variables when it starts. Set one on the deployment, and Kubernetes restarts Pyo with the new value:

sudo k3s kubectl -n pyo-system set env deployment/pyo PYO_ALLOW_SIGNUP=true

To see what's set now:

sudo k3s kubectl -n pyo-system set env deployment/pyo --list

Pyo has two deployments, and some settings belong on both. pyo serves the dashboard and API, and pyo-worker runs deploys, backups and other background work.

Updating Pyo puts back the values it sets in its own manifest, like PYO_REGISTRY_PUSH. Variables you've added, like PYO_ALLOW_SIGNUP and PYO_COOKIE_SECURE, are kept.

Installation settings

These are the ones you're most likely to change.

VariableDefaultWhat it does
PYO_ALLOW_SIGNUPfalseLets anyone who can reach the dashboard create an account. See sign-ups.
PYO_COOKIE_SECUREfalseSends the session cookie over HTTPS only. Turn it on when the dashboard is behind HTTPS.
PYO_PASSKEY_ORIGINSNot setMore HTTPS addresses of the dashboard where passkeys work, comma-separated, like https://pyo.example.com.
PYO_ADMIN_PASSWORDGeneratedThe first admin's password. It's only read when an installation has no accounts yet, and it has to be replaced on first sign-in.
PYO_ALLOW_PRIVATE_NETWORKStrueLets teams use addresses on private networks, like your LAN or tailnet, for registry logins, backup buckets and secret stores. Pyo's own server and cluster network are never allowed. Set false when people you don't fully trust run teams, as Pyo Cloud does.
MAXMIND_LICENSE_KEYNot setYour GeoLite2 license key, to keep visitor location data up to date. Without one, Pyo uses the copy it ships with.

Connections

Pyo sets these when it's installed, so you'll rarely need to touch them.

VariableDefaultWhat it does
DATABASE_URLRequiredThe PostgreSQL database that holds Pyo's data.
PYO_DB_POOLBy memoryThe most database connections each process opens. By default it follows Pyo's database, which is sized by the server's memory. See Memory tiers.
CLICKHOUSE_URLNot setThe ClickHouse server for observability. Without it, observability is off.
CLICKHOUSE_USERdefaultThe ClickHouse user.
CLICKHOUSE_PASSWORDEmptyThe ClickHouse password.
PYO_AGENT_API_URLhttp://pyo.pyo-system.svc:8000The address agents connect to.
PYO_LOCAL_URLhttp://127.0.0.1:8000How the install command for This machine reaches Pyo, and where it downloads its script.
PYO_SCRIPTS_URLPyo's own addressWhere the commands for connecting other servers download their scripts. When not set, they download from Pyo itself, at the address the command gives the server, like your tailnet or public URL. Without one, they use https://pyo.dev. This machine always downloads from Pyo on it.
PYO_AGENT_SELF_UPDATEtrueLets agents update their own limits when Pyo is updated, on clusters connected while it's on. Set false to update them yourself. The cluster's page then shows Update needed with the command to run on its server. See Securing Pyo.
PYO_REGISTRY_PUSHThis machine's Tailscale address, port 5005Where servers and builders on other machines reach Pyo's registry, with a login. Not set without Tailscale.
PYO_REGISTRY_PULL127.0.0.1:5005The registry address servers pull images from.
PYO_REGISTRYpyo-registry.pyo-system.svc.cluster.local:5000The registry itself, which only Pyo reaches.
PYO_TAILNET_NAMENot setThis machine's tailnet name. With it, Pyo is at https://<name>:8443, its registry at <name>:5443, and sign-in cookies are HTTPS-only. Without it, Pyo is only at http://localhost:8000.
PYO_BACKUP_STORE_URLNot setThe backup store on this cluster.
PYO_BACKUP_STORE_ADMIN_URLNot setThe backup store's admin API, where Pyo sets it up and makes each team's bucket and key.
PYO_BACKUP_STORE_ADMIN_TOKENNot setThe token for the backup store's admin API.
PYO_BACKUP_STORE_ACCESS_KEYNot setPyo's own key to the backup store. Teams never get it.
PYO_BACKUP_STORE_SECRET_KEYNot setPyo's own secret key to the backup store.

Running Pyo

VariableDefaultWhat it does
PYO_ROLEallWhat a process does: web, worker or all.
LISTEN_ADDR0.0.0.0:8000The address Pyo listens on.
PYO_WEB_DIRdash/dist/clientWhere the built dashboard is.
PYO_AGENT_IMAGE127.0.0.1:5005/pyo/agent:devThe agent image for new clusters.
PYO_BUILDER_IMAGE127.0.0.1:5005/pyo/builder:devThe image for new builders.
RUST_LOGpyo_api=info,tower_http=infoHow much Pyo writes to its logs.

Encryption

These decide how your secrets are protected.

VariableDefaultWhat it does
PYO_SECRET_KEYSet by the installerThe master key, as 64 hex characters. Save a copy.
PYO_SECRET_KEY_PREVIOUSNot setThe old master key, while you rotate it.
PYO_KMSNot setSet to openbao or vault to protect team keys with OpenBao or Vault.
OPENBAO_ADDRNot setYour OpenBao or Vault address. VAULT_ADDR works too.
OPENBAO_TOKENNot setYour OpenBao or Vault token. VAULT_TOKEN works too.
PYO_KMS_MOUNTtransitThe Transit secrets engine's mount.
PYO_KMS_KEYpyoThe Transit key's name.

Rotating the master key only moves team variables to the new key. Saved credentials, like your GitHub App, registry and SMTP passwords and certificate keys, and existing backups of Pyo itself can't be opened with a new key. Keep PYO_SECRET_KEY as it is, even with OpenBao or Vault, unless the key has leaked. If it has, add those credentials again afterwards and take a new backup.

Updates

Set these on both pyo and pyo-worker.

VariableDefaultWhat it does
PYO_UPDATE_CHECKOnSet to off to stop looking for new releases, for a server that can't reach GitHub.
PYO_UPDATE_SOURCEhttps://api.github.com/repos/upsetdev/pyoA mirror of GitHub's release API to check instead.

The install command reads these too, when you run it:

VariableWhat it does
PYO_VERSIONThe release to install, like 0.1.0. Takes the place of --version.
PYO_YESSet to 1 to update without asking, after you've read the release's notices. Takes the place of --yes.
PYO_UPDATE_SOURCEThe mirror to get the release from, as above.

See Updating Pyo.

CLI

Set these on the machine where you run pyo, like in CI.

VariableWhat it does
PYO_URLYour Pyo's address. Takes the place of --url.
PYO_TOKENAn API key. Takes the place of --token.
PYO_INSTALL_DIRWhere the CLI installer puts pyo.
NO_COLORTurns off colored output.

pyo login saves your sign-in to ~/.config/pyo/config.toml.

Inside your services

Pyo doesn't add PYO_ variables to your services. It only sets PORT to the service's first port, unless you've set PORT yourself. Everything else is what you add under environment variables.

Set by Pyo

Pyo sets these itself, so leave them as they are.

VariableWhat it does
PYO_API_URLThe address agents and builders connect to.
PYO_AGENT_TOKENHow an agent signs in to Pyo.
PYO_BUILDER_TOKENHow a builder signs in to Pyo.
PYO_GEOIP_DBWhere the visitor location data is kept.
PYO_TAILNET_API_URLPyo's address on your Tailscale network.

What runs on your server

Pyo keeps its own parts in the pyo-system namespace:

sudo k3s kubectl -n pyo-system get pods
NameWhat it does
pyoThe dashboard and API.
pyo-workerRuns deploys, backups and other background work.
pyo-agentCarries out Pyo's instructions on each cluster. It connects out to Pyo, so nothing has to connect in to it.
pyo-postgresPyo's own data, in the pyo database.
pyo-clickhouseRequests and metrics for observability, in the pyo_observability database.
pyo-vectorCollects request logs on each server and passes them to Pyo.
pyo-backup-storeS3-compatible storage on this cluster (Garage, a single instance), the default destination for backups. Each team's key reaches only its own bucket.
registryHolds the images your builds produce. Only Pyo reaches it.
pyo-hostPuts the dashboard on port 8000 and the registry on port 5005, on the machine itself and its Tailscale address. On the Tailscale address, the registry needs a login.
buildkitdBuilds Pyo's own images when Pyo is built from source. Stopped otherwise. Your images are built by builders.
pyo-builder-*Your builders. Their BuildKit runs only while there are builds.

Pyo also relies on these, outside that namespace:

NameWhereWhat it does
k3sThe hostThe Kubernetes distribution everything runs on.
Gateway controller (Envoy Gateway)pyo-gatewaySets up the gateway's proxies, and keeps your sites' certificates. See Pyo Gateway.
Gateway proxy (Envoy)pyo-gatewayOne on each server, except build-only servers. Takes traffic on ports 80 and 443, and public TCP and UDP ports, and routes it to your services. Published on the servers only while the cluster accepts public traffic.
pyo-tailnet Servicepyo-gatewayOn the machine Pyo runs on. Takes connections to its tailnet address on ports 8443 and 5443 straight to the gateway, so the gateway sees each device's tailnet address. Written by the machine's daily pyo-tailnet-cert timer.
Gateway APIThe clusterThe standard routing objects Pyo Gateway reads. k3s 1.37 and later include it. On older k3s, Pyo installs it, and k3s takes it over when you upgrade to 1.37. It stays if you remove Pyo.
pyo-wafpyo-gatewayThe web application firewall, only while a service on the cluster has it on.
cert-managercert-managerIssues HTTPS certificates. A certificate's Secret is deleted with it.

In pyo-system, network policies turn away everything Pyo doesn't need: Pyo's database, ClickHouse, the registry and the backup store's admin API only answer Pyo itself. Pyo's dashboard and API answer the gateway, Pyo's own parts and the servers, never your services' pods directly: they reach it through its public or tailnet address, like anyone else. Your services reach the backup store's S3 API with a key.

If the gateway fails to install, the cluster's page says so. Its installer's log has the reason:

sudo k3s kubectl -n kube-system logs job/helm-install-envoy-gateway

Each project gets its own namespace, named when you create the project. Pyo labels what it creates with pyo/project-id and pyo/service-id.

Pyo manages everything here. Changes you make with kubectl can be overwritten by the next deploy or update.

k3s

On a machine without k3s, the installer installs the version that release of Pyo was tested with: v1.37.2+k3s1 for this release. It downloads k3s from its GitHub release and checks both k3s and its installer against checksums fixed in the release. If one doesn't match, it stops before anything is installed.

Servers you add to a cluster get the cluster's own k3s version, checked against that k3s release's published checksums. Tailscale, and the NVIDIA Container Toolkit for GPUs, come from their vendors' package repositories, whose signing keys are checked by fingerprint.

To use another k3s version, install k3s yourself before installing Pyo. Pyo uses the k3s it finds and doesn't change its version, including when Pyo updates.

On a machine with less than 3.5 GiB of memory, the k3s that Pyo installs gets a soft memory limit, so it frees memory sooner instead of growing. The limit is 40% of the memory, and at least 768 MiB. With less than 2.75 GiB and no swap, Pyo also turns on compressed swap in memory (zram, pyo-zram.service), half the size of the memory, for k3s itself. Your services don't swap. Both are worked out again each time the machine starts, by /usr/local/lib/pyo-memory.sh (k3s runs it from /etc/systemd/system/k3s.service.d/pyo-memory.conf). Uninstalling Pyo with k3s removes them. From 3.5 GiB of memory, k3s gets neither.

At any size, the same script sets aside memory for k3s and the system, so containers are only scheduled into what's left. It writes kubelet's kube-reserved and system-reserved to /etc/rancher/k3s/config.yaml.d/50-pyo-memory.yaml each time k3s starts. See Memory tiers for the amounts. Servers you add keep all their memory for containers.

Memory tiers

Pyo reads the server's memory each time a part of it starts, so a server you resize gets the matching settings after a restart:

Memoryk3sSet aside for k3s and the systemPyo's databaseConnections per process
Under 1.5 GiBSoft memory limit and compressed swap256 MiB16 MB of cache, 40 connections5 for pyo, 12 for pyo-worker
1.5 to 2.75 GiBSoft memory limit and compressed swap512 MiB16 MB of cache, 40 connections5 for pyo, 12 for pyo-worker
2.75 to 3.5 GiBSoft memory limit512 MiB16 MB of cache, 40 connections5 for pyo, 12 for pyo-worker
3.5 to 8 GiBAs it is768 MiB32 MB of cache, 80 connections10 for pyo, 16 for pyo-worker
8 GiB or moreAs it is1 GiB64 MB of cache, 120 connections15 for pyo, 24 for pyo-worker

Pyo's own parts ask for about the memory they hold at rest, and may use more up to their limits. On a single server with databases set up, they and k3s's DNS and metrics ask for around 890 MiB.

PYO_DB_POOL sets the connections per process instead. Keep two of each process within the database's connections, since an update runs the old and new ones side by side.

Observability is a setting, not a tier: a new install under 3.5 GB starts with it off, and after that it changes only when an admin changes it.

Agent policy

To limit what the agent may do on a cluster, create a pyo-policy ConfigMap in pyo-system on that cluster. The agent only reads it, so only someone with access to the server can change it.

KeyValueEffect
terminaloffNo terminals into services.
forceRemoveServersoffServers are only removed after their services have moved away.
readOnlytrueThe agent reports, and changes nothing.
sudo k3s kubectl -n pyo-system create configmap pyo-policy --from-literal=terminal=off

The team's Security page shows each cluster's policy.

Secrets

Pyo keeps its keys as Kubernetes Secrets in pyo-system.

SecretWhat it holds
pyo-secretsThe master key. Back this up, and never delete it.
pyo-adminThe password the installer printed. It stops working once you've set up your account.
pyo-agentHow this cluster's agent signs in to Pyo.
pyo-vectorHow log collection connects to Pyo.
pyo-postgresThe password and address for Pyo's database.
pyo-clickhouseThe ClickHouse password.
pyo-backup-storeThe backup store's internal secret and admin token, and Pyo's own key to it. Teams' keys are kept in Pyo's database, encrypted.
pyo-bootstrapThe key this machine uses to connect itself, and to prove it's the cluster Pyo runs on. Only on that cluster.
pyo-geoipYour GeoLite2 license key, if you've added one.

On this page

Edit page