Application configuration
Define services, variables, resources and storage in a versioned TOML file.
The dashboard, CLI and repository sync submit the same versioned application specification. Export the configuration from an application, edit it in a repository or the dashboard, then review the plan before deploying.
Every file needs schema_version = 1, a name and at least one service with an image. Unknown fields are rejected. Project and environment come from the selected dashboard scope or CLI context, not from Kubernetes namespace settings inside the file.
Service settings#
| Field | Use |
|---|---|
image |
OCI image tag or digest; tags are resolved during deployment. |
port |
Main private TCP port and the target for optional public HTTP. |
public |
Add HTTP ingress; false by default. |
healthcheck |
Optional HTTP readiness path; a declared port otherwise uses TCP readiness. |
size |
A fixed CPU and memory profile; small by default. |
replicas |
From 1 to 20 for ordinary services. Serverless HTTP services can sleep at zero. |
depends_on |
Services that must become ready first during rollout. |
architecture |
Optional amd64 or arm64 image and node restriction. |
Dependency ordering does not replace application retries after startup. An HTTP readiness check controls when a service can receive traffic. Hakopod does not also use it as a liveness probe to restart the container.
Ordinary variables and secrets#
Each service has its own environment variables. Ordinary values appear in exported TOML and configuration history:
[services.web.env]
API_URL = "http://api:8080"
LOG_LEVEL = "info"
Store credentials as application-scoped secrets first, then bind their names explicitly to the services that need them:
[services.api.secrets]
DATABASE_URL = { ref = "database-url" }
These are configuration excerpts to add to existing service definitions. Secret values are write-only and do not appear in TOML, secret-list responses or revision history. Changing a secret requires a restart or deployment to refresh a container's environment. Removing a binding cannot erase a value already inside a running process.
Use the service's Environment and Secrets sections to edit these directly. Store credentials under Secrets even when their variable names look harmless. Validation cannot identify every secret entered as an ordinary variable.
External secret providers#
Self-hosted Free can read Vault/OpenBao KV v2 and Infisical. An installation administrator adds a provider in Settings > Secret providers, sets its project/environment access, and supplies a restricted upstream credential. Saved credentials are never displayed again.
Bind a permitted value without putting it in TOML:
[services.api.secrets]
DATABASE_URL = { provider = "production-vault", path = "shop", key = "DATABASE_URL" }
The path is relative to the provider's configured root. Use either a native ref or an external provider reference for each value. Granting provider access makes permitted values below that root available to the selected projects, so configure separate providers for projects that should not share access.
A deployment or service restart reads a fresh snapshot. There is no background refresh. If a provider is unavailable or a value is missing, deployment fails before changing existing workload secrets; the last good values stay in place. Rotate expiring provider credentials yourself. HTTPS verification remains enabled, and private endpoints require an administrator-approved network range.
See the provider setup and limits. Other external secret stores are not yet supported.
Resource profiles#
| Profile | CPU request / limit | Memory request / limit |
|---|---|---|
| small | 100m / 500m | 128 MiB / 256 MiB |
| medium | 250m / 1 CPU | 256 MiB / 512 MiB |
| large | 500m / 2 CPU | 512 MiB / 1 GiB |
| compute | 1 / 4 CPU | 2 GiB / 4 GiB |
| gpu | 2 / 8 CPU | 8 GiB / 16 GiB |
Kubernetes uses requests to decide where a pod can run. Limits cap its resource use. A profile does not reserve capacity your nodes do not have. A normal rolling update may need one additional replica. Persistent services use one replica and a Recreate update, which stops the old pod before starting its replacement. Plan for downtime.
Storage and filesystem permissions#
Use a service volume for one persistent data directory, or named application volumes when you need explicit mounts. This excerpt defines a named volume and mounts it in an existing API service:
[volumes.uploads]
size_gib = 10
[services.api]
image = "your-registry.example/api:1"
port = 8080
run_as_user = 10001
run_as_group = 10001
fs_group = 10001
read_only_root_filesystem = true
mounts = [{ volume = "uploads", mount_path = "/app/uploads" }]
temporary_mounts = [{ mount_path = "/tmp", size_mib = 32, memory = true }]
Replace the illustrative image with your own. The image must support the selected non-root identity and writable paths. A memory-backed temporary mount counts against the container's memory limit and disappears when the pod is replaced.
Named volumes default to ReadWriteOnce. Sharing across services requires ReadWriteMany, an explicitly selected capable storage class and matching filesystem groups. Hakopod does not install a shared-storage driver for you. Existing claim size, class and access mode cannot change through an ordinary deployment.
Mounts cannot expose host directories or protected system paths. Removing a service retains its persistent claim; rolling back configuration does not rewind its data. Use Backups separately.
The full schema reference includes autoscaling, private ports, certificate mounts, approved AWS identities and exact limits.