kyper.yml Reference
Complete field reference for the configuration file that describes your app to Kyper.
Full example
name: "my-cool-app"
version: "1.2.0"
category: productivity
docker:
dockerfile: "./Dockerfile"
processes:
web: "bundle exec puma -C config/puma.rb"
worker: "bundle exec sidekiq -C config/sidekiq.yml"
deps:
- postgres: "15"
storage_gb: 50
- redis
storage:
mounts:
- path: storage
storage_gb: 5
- path: uploads
env:
- OPENAI_API_KEY
capabilities:
- email
- payments
hooks:
release: "bundle exec rails db:prepare"
on_deploy: "bundle exec rails cache:warmup"
on_update: "bundle exec rails cache:warmup"
healthcheck:
path: "/up"
initial_delay: 15
interval: 30
timeout: 10
pricing:
one_time: 49.00
subscription: 12.00
resources:
min_memory_mb: 512
min_cpu: 0.5
name required
The unique identifier for your app on Kyper. Used to construct your app's URL (kyper.shop/apps/my-cool-app). Cannot be changed after first push without creating a new app.
name: "invoice-hero"
version required
The current version of your app, following semantic versioning (MAJOR.MINOR.PATCH). Bump this on every push — Kyper enforces that each pushed version is greater than the previous.
Version update behaviour: Patch/minor bumps are auto-deployed to all running consumer instances. Major bumps notify consumers and require opt-in before their deployment upgrades. See Versioning.
version: "1.0.0"
category required
Used for marketplace filtering. Choose the most relevant category.
developer_tools,
productivity,
finance,
health,
media,
education,
business_operations,
data_analytics,
gaming
docker required
Configuration for the Docker build. Kyper builds your image from source — you do not need a container registry account.
Important: docker.image is rejected. Kyper always builds from your Dockerfile to ensure security scanning can run.
| Key | Type | Default | Description |
|---|---|---|---|
| docker.dockerfile | string | "./Dockerfile" | Path to your Dockerfile, relative to project root |
processes required
A map of process type names to the commands that run them. At least one web process is required.
The web process must bind to $PORT.
Each process runs independently using the same Docker image with a different command. Only the web process receives external traffic — all other processes run in the background without exposed ports.
processes: web: "bundle exec puma -C config/puma.rb" worker: "bundle exec sidekiq -C config/sidekiq.yml" scheduler: "bundle exec clockwork config/clock.rb"
Port 3000 is required. Your web process must listen on $PORT (always set to 3000).
Add EXPOSE 3000 to your Dockerfile. Apps that bind to port 80 or 443 will fail with "permission denied" at deploy time.
Max 5 processes. Names: [a-z0-9-], max 63 chars. All share the same image, env vars, and deps. Adding/removing processes between versions is handled automatically.
deps
Backing services that Kyper provisions alongside your app. Each dep gets a managed instance with connection details injected as environment variables.
Formats
# Plain string (platform default version)
deps:
- postgres
- redis
# Version-pinned hash
deps:
- postgres: "15"
- redis: "6"
# With custom storage
deps:
- postgres: "15"
storage_gb: 50 # default: 20 GB
- redis
App-local persistent storage
Use storage.mounts for framework-local files such as Rails Active Storage local, Django media uploads, Laravel storage/app, or explicit upload directories. Relative paths resolve against the image working directory. Up to 5 mounts are allowed; storage_gb is optional and must be 1-10 GB.
storage:
mounts:
- path: storage
storage_gb: 5
- path: /app/uploads
Supported versions
| Dep | Allowed versions | Default | Injected variable |
|---|---|---|---|
| postgres | 14, 15, 16 | 16 | DATABASE_URL |
| mysql | 8 | 8 | DATABASE_URL |
| redis | 6, 7 | 7 | REDIS_URL |
| elasticsearch | 8 | 8 | ELASTICSEARCH_URL |
| opensearch | 2 | 2 | OPENSEARCH_URL |
| s3 | none (Kyper-managed) | latest | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_BUCKET, AWS_ENDPOINT_URL |
Storage is billed at $0.10/GB/month and included in the consumer-facing subscription price. Dep containers include readiness healthchecks — your app won't start until all deps are accepting connections.
S3-compatible object storage (s3)
Provisions a fully S3-compatible object storage service. Any AWS SDK (ActiveStorage, Shrine, boto3, etc.) works against it unmodified.
The app bucket is auto-created on first write. Data persists across app updates. Version pinning not supported.
pricing at least one required
At least one pricing option must be set. Prices must be ≥ $1.00. Kyper takes a 15% commission; your net payout is 85% minus Stripe processing fees.
| Key | Type | Description |
|---|---|---|
| pricing.one_time | decimal (USD) | One-time purchase price. Consumer downloads the latest approved zip. |
| pricing.subscription | decimal (USD/month) | Monthly hosted subscription price. Kyper deploys and manages the app. |
env
Declares the environment variable keys that consumers must supply before their deployment can start. Use this for API keys, third-party tokens, and other secrets that Kyper cannot inject automatically.
env: - OPENAI_API_KEY - STRIPE_SECRET_KEY - SENDGRID_API_KEY
Keys listed here are names only (no values). Consumers enter the values through the deployment configure page. Values are stored encrypted and injected at provision time.
Auto-injected variables
These are always available — do not list them in env:.
| Variable | Value |
|---|---|
| SECRET_KEY_BASE | Securely generated per-deployment |
| PORT | The port your web process must listen on |
| KYPER_DEPLOYMENT_ID | The UUID of the consumer's deployment |
| ORIGIN | The deployment's public URL (e.g. https://abc123.apps.kyper.shop) — for WebAuthn, OAuth callbacks, CORS |
| DATABASE_URL | Injected if postgres or mysql dep declared |
| REDIS_URL | Injected if redis dep declared |
| ELASTICSEARCH_URL | Injected if elasticsearch dep declared |
| OPENSEARCH_URL | Injected if opensearch dep declared |
| AWS_ACCESS_KEY_ID | Injected if s3 dep declared |
| AWS_SECRET_ACCESS_KEY | Injected if s3 dep declared |
| AWS_BUCKET | "app" — injected if s3 dep declared |
| AWS_ENDPOINT_URL | Injected if s3 dep declared — points to your deployment's object storage service |
capabilities
Declares user-facing services your app needs. Kyper manages email, Google Maps, and Mapbox by default, while payments and SMS require the buyer to connect or provide their own service account before deploy.
capabilities: - email - maps - mapbox - payments - sms
Available capabilities
| Capability | Default source | Provider |
|---|---|---|
| Kyper-managed | Resend | |
| maps | Kyper-managed | Google Maps |
| mapbox | Kyper-managed | Mapbox |
| payments | Buyer-managed setup | Stripe |
| sms | Buyer-managed setup | Twilio |
Legacy integrations: is no longer supported; use capabilities:.
Do not list capability-provided keys in env: — they are injected automatically.
Managed email injects both RESEND_API_KEY and a verified sender address KYPER_MAIL_FROM. Read your "from" address from KYPER_MAIL_FROM; do not declare a sender in env:. See Services.
hooks
Commands that Kyper runs at key lifecycle points. release runs as a one-shot Job before your app pods start (after deps are ready). on_deploy and on_update run inside a healthy web pod, after pods are ready.
| Key | When it runs |
|---|---|
| hooks.release | Before app pods start, after deps (postgres, redis, etc.) are ready. Use for migrations and one-time setup. |
| hooks.on_deploy | After pods are healthy on a new deployment. Use for cache warmup, post-deploy notifications, etc. |
| hooks.on_update | After pods are healthy on a version update. Same execution model as on_deploy; auto-rollback on failure. |
Apps with a database dep should always set hooks.release for migrations. Do not use on_deploy for migrations — it runs after pods are healthy, so it can't help an app that crashes on first boot waiting for its schema. Kyper warns at push time if a postgres/mysql dep is declared without hooks.release.
# Rails hooks: release: "bundle exec rails db:prepare" on_deploy: "bundle exec rails cache:warmup" # Prisma / Node hooks: release: "prisma migrate deploy"
healthcheck
Controls how Kyper polls your app's health endpoint. Checked during provisioning, during updates, and on an ongoing schedule (3 consecutive failures mark the deployment as failed).
| Key | Type | Default | Description |
|---|---|---|---|
| healthcheck.path | string | /up | HTTP path to GET. Must start with /. |
| healthcheck.initial_delay | integer | 10 | Seconds to wait before the first health check. Increase this if your app takes a while to boot (e.g., Rails with Thrust). Must be 0–300. |
| healthcheck.interval | integer | 10 | Seconds between checks. Must be 10–300. |
| healthcheck.timeout | integer | 5 | Seconds before a check is considered failed. Must be positive. |
resources
Hints that Kyper uses to select the right server size for hosted subscriptions. These are minimums — Kyper may provision a larger size if needed. Consumers can also resize after deployment.
| min_memory_mb | min_cpu | Suggested tier |
|---|---|---|
| 512 | 0.5 | Hobby ($5/mo) |
| 512 | 1 | Hobby ($5/mo) |
| 1024 | 1 | Pro ($15/mo) |
| 2048 | 2 | Turbo ($25/mo) |
| 4096 | 2 | Turbo ($25/mo) |
CPU hints can be fractional. These are hints — consumers pick their tier at subscribe time. See Pricing.
Validation rules
Kyper validates kyper.yml at push time. Run kyper check locally to catch issues before pushing. Common rejection reasons:
| Rule | Error message |
|---|---|
| name contains uppercase or special chars | name must be lowercase alphanumeric + hyphens |
| version not semver | version must follow semver (MAJOR.MINOR.PATCH) |
| docker.image present | docker.image is not allowed — Kyper builds from source |
| web process missing | processes must include a 'web' key |
| More than 5 processes | processes is limited to 5 entries |
| Invalid process name | process name must contain only lowercase letters, digits, and hyphens |
| Unknown dep listed | unknown dep 'mongodb' — allowed: postgres, mysql, redis, elasticsearch, opensearch, s3 |
| No pricing set | at least one of pricing.one_time or pricing.subscription must be present |
| Unknown category | category 'saas' is not a valid category |