# Ownkube: full content dump for AI agents > Ownkube is a cloud for personal software. One prompt to production: tell your coding agent to deploy a repo, and it ships a web app, worker, scheduled job, managed Postgres database, or Valkey cache to a live HTTPS URL. No cloud account and no config file. The same app can later move to your own AWS account with no rewrite. This file concatenates the homepage summary, pricing, and every blog post in one document so agents can ingest the site in a single fetch. - Site: https://ownkube.io - Signup: https://app.ownkube.io/login - Login: https://app.ownkube.io/login - Short index: https://ownkube.io/llms.txt - Agent setup runbook: https://ownkube.io/prompt.md - MCP server: https://app.ownkube.io/api/mcp - Docs: https://ownkube.io/docs - Docs index for agents: https://ownkube.io/docs/llms.txt (full docs dump: https://ownkube.io/docs/llms-full.txt) - Sitemap: https://ownkube.io/sitemap-index.xml --- ## Product summary Ownkube is a cloud for personal software, built for individual developers and small teams. Deploy web apps, background workers, scheduled jobs, managed Postgres databases, and Valkey caches. Connect a GitHub or GitLab repo and Ownkube builds it from source, with or without a Dockerfile. Any language, any framework. Ownkube Compute is where apps run by default: no cloud account, no config file, and no subscription to start. It runs in the Mumbai and Germany regions, with more regions on request. If you ever need it, the same app moves to your own or a customer's AWS account with no rewrite, and its logs, metrics, and health checks come with it. GCP and Azure are coming soon. ## One prompt to production Tell any coding agent: "Fetch https://ownkube.io/prompt.md and follow it to deploy this repo on Ownkube." 1. The agent connects the Ownkube MCP server (https://app.ownkube.io/api/mcp) and installs okctl with its skill. 2. You sign in, connect GitHub, and fund the wallet. 3. The agent proposes the deploy, you approve it, and the app goes live on an ownkube.app address with HTTPS. Agents that connect over MCP: Claude Code, Cursor, Codex, GitHub Copilot, Windsurf, Zed, and Claude Desktop. Per-agent setup: https://ownkube.io/agents. ## What you can deploy - Web apps and APIs, in any language or framework - Background workers - Scheduled jobs on a cron schedule, with run history and Run now - Managed Postgres databases, private by default. Optional high availability (a primary plus up to two standby instances with automatic failover), automatic backups with point-in-time recovery (opt-in per database), and opt-in public access with its own TLS certificate. - Managed Valkey caches (Redis-compatible), private by default, with an optional public rediss:// endpoint. Ownkube handles builds, deploys, automatic HTTPS, horizontal autoscaling on CPU and memory, logs, metrics, environments with promotion, and one-click rollbacks. ## Pricing - Prepaid wallet: start on Pay as you go by topping up from $5 (no subscription), or pick a plan (Personal $5 per month, Team $20 per month) that loads that much wallet credit each month and unlocks more. Per-minute usage draws the wallet down. Unused credit rolls over; the build-minutes allowance resets monthly. Top up any time from a $5 minimum. - Metered rates: about $0.00041 per vCPU-minute and $0.0001 per GiB-minute, roughly a third under the equivalent Railway bill. Workloads that want a flat price can opt into a reserved Spark or Core box. - Databases run on a database box from $4 per month plus $0.25 per GB of storage. Caches run on a cache box from $2 per month, with no storage charge. - Bandwidth: outbound traffic bills per GB at the region's rate ($0.13 per GB Mumbai, $0.02 per GB Germany). Inbound and in-region traffic are free. - Build minutes: Personal includes 300 a month, Team 1,500. Pay as you go pays per build minute from the first minute. - Spend limits: set a monthly budget and choose to be notified or to pause everything at the cap (data is kept). Spending alerts email the account owner at 50%, 80%, and 100%. - Enterprise: custom. Volume pricing, SSO, an SLA, and dedicated support, set up with our team. - Full details: https://ownkube.io/pricing (markdown: https://ownkube.io/pricing.md). ## CLI (okctl) okctl is the Ownkube command-line interface. It drives the platform from your terminal, so nothing about Ownkube is click-only. - Install: Homebrew (brew install ownkube/tap/okctl), pre-built release binaries for macOS, Linux, and Windows on amd64 and arm64, or go install github.com/ownkube/okctl@latest. - Log in from your browser (okctl login), then inspect environments and deployments (status, logs, revisions, connection details), registries, and organizations. - Scriptable: every read command speaks JSON (add -o json to any read command). - Coding agents: run "npx skills add ownkube/ownkube-cli" to install the CLI skill so your agent can drive Ownkube from the terminal. - Download page: https://ownkube.io/cli. Docs: https://ownkube.io/docs/cli. Source: https://github.com/ownkube/ownkube-cli. ## How Ownkube compares | Capability | Ownkube | Vercel | Render | Railway | |---|---|---|---|---| | Deploy with no cloud account | Yes | Yes | Yes | Yes | | Any workload (web, worker, cron, database, cache) | All of it | Frontend + functions | Yes | Yes | | Managed Postgres included | Built in, with HA, backups, and PITR | Via add-ons | Yes | Yes | | Billing | Per minute, prepaid wallet | Plan tiers | Instance hours | Per minute | | Move the same app to your own AWS | Yes | No | No | No | ## FAQ **Do I need a cloud account to start?** No. Ownkube runs your app on its own infrastructure, so you can deploy with no AWS account, no config file, and no subscription. **Can my coding agent deploy for me?** Yes. Point it at https://ownkube.io/prompt.md. It connects the MCP server and installs okctl; you sign in, fund the wallet, and approve the deploy. **What can I deploy?** Web apps, background workers, scheduled jobs, managed Postgres, and Valkey caches. Any language, any framework. **What domain do my apps get?** Every app gets an ownkube.app address with automatic HTTPS. **Which regions does Ownkube run in?** Mumbai and Germany, at the same prices except bandwidth, with more regions on request. **Can I cap my spend?** Yes. Set a monthly budget and choose to be notified or to pause everything when month-to-date spend reaches it. **Can I run the same app in my own cloud?** Yes. The same app moves to your own or a customer's AWS account with no rewrite, and logs, metrics, and health checks come with it. GCP and Azure are coming soon. --- # Blog posts (full text) --- ## How to run scheduled and cron jobs on Ownkube > Deploy a scheduled job on Ownkube: set a cron expression, time zone, retries, and overlap handling, then trigger and watch runs from the Runs tab. - Canonical: https://ownkube.io/blog/run-scheduled-cron-jobs - Markdown: https://ownkube.io/blog/run-scheduled-cron-jobs.md - Published: 2026-09-08 - Author: Abhishek Jaiswal - Category: How-To - Tags: cron, scheduled-jobs, jobs, tutorial, ownkube-compute A nightly cleanup script, a report that emails itself every Monday, a cache warmer that runs every five minutes: all of it needs somewhere to run on a schedule without you leaving a laptop open. Ownkube runs these as a first-class deployment type, a scheduled job, with the same git-push deploy as any [web app](/blog/deploy-a-web-app-in-under-a-minute). This guide deploys a scheduled job on Ownkube Compute, walks through every setting on its Schedule and Execution tabs, and shows how to watch and control runs from the Runs tab. **TL;DR** - A scheduled job is its own deployment type (`job`), built from a repo or image the same way a web app is, but with no port and no public URL. - The Schedule section sets the [cron expression](https://crontab.guru/), an optional time zone, and how overlapping runs are handled (skip, allow, or replace). - The Execution section sets retry limit, run timeout, parallel task count, completions, and how long to keep finished runs around. - The Runs tab shows live and past runs with duration and pod-level failure detail, plus a Run now button and a per-run cancel. - Jobs run on the same [metered wallet](/pricing) as everything else on Ownkube Compute, so a job that finishes in ten seconds barely costs anything. ## Deploy the job 1. From your dashboard, click New (or press Cmd+K) and choose Scheduled job as the deployment type. 2. Connect the GitHub or GitLab repository that has the script, or point at an existing image. A Dockerfile is optional: if the repo has one, Ownkube builds from it; if not, Ownkube detects the language and builds automatically. 3. Set the command and arguments the job runs, for example `python` and `send_report.py`. Leave both empty to use the image's own entrypoint. 4. Deploy. The job is created paused by default until you set a schedule in the next step. ## Set the schedule Open the job's Settings tab and go to the Schedule section. - **Cron expression**: standard [5-field cron](https://man7.org/linux/man-pages/man5/crontab.5.html) (`*/5 * * * *`) or a shorthand like `@hourly`. The field validates as you type, so an invalid expression blocks save. - **Time zone** (optional): an IANA name like `America/New_York` or `UTC`. Leave it blank to run in the cluster's default time zone. - **Overlapping runs**: choose what happens if a run is still going when the next one is due. - *Skip if running* (the default): the new run is skipped while the previous one finishes. - *Allow overlap*: runs can stack if one is slow. - *Replace running*: the in-progress run is canceled and the new one starts immediately. - **Start deadline**: skip a run entirely if it can't start within this many seconds of its scheduled time (useful if the cluster was briefly busy). - **Keep successful / failed runs**: how many finished runs of each kind stay in history before Ownkube prunes them. The Active/Paused switch at the top of the card pauses the schedule without deleting the job, handy for maintenance windows. ```text # Every day at 2:00 AM UTC 0 2 * * * # Every 15 minutes */15 * * * * # Every Monday at 9:00 AM, in America/New_York 0 9 * * 1 ``` ## Configure execution behavior The Execution section controls what happens inside each run: - **On failure**: *Never* leaves a failed pod in place for inspection and lets the job retry with a fresh one; *On failure* restarts the same pod in place. - **Retry limit**: how many times a failed run retries before it's marked failed for good. - **Run timeout (seconds)**: cancels a run that's still going after this long, so a hung script doesn't run (and meter) forever. - **Clean up after (seconds)**: automatically removes a finished run's records after this window. - **Parallel tasks** and **Completions**: run more than one copy of the task at once, and require a number of successful completions before the run counts as done. Most jobs leave both at 1. You can also override resource limits for the job on its Resources tab, separate from any web or worker deployment in the same project. ## Trigger and monitor runs The Runs tab is the operational view once the schedule is live. - A **Schedule** summary card shows whether the job is active or paused, the cron expression, its time zone, and the last run and last successful run timestamps. - **Run now** fires an off-schedule run immediately, useful for testing a new script or manually catching up a report. - Each row in **Recent runs** shows status (running, succeeded, failed, or pending), a live duration for runs in progress, and a **cancel** action while a run is in flight. - A failed run expands to show which pod failed, its exit code, and the failure reason, so you don't have to open the Logs tab just to see why a run went red. ```bash # From the CLI: list a job's run history okctl deploy job-runs list # Trigger an off-schedule run okctl deploy job-runs trigger # Cancel a run that's stuck okctl deploy job-runs cancel ``` ## What it costs Scheduled jobs run on the same prepaid wallet as the rest of Ownkube Compute: both CPU and memory meter on actual per-minute usage, so a job that runs for ten seconds every hour costs close to nothing, and a report script that runs for two minutes once a night is barely a rounding error against your monthly plan credit. If you're also running the same account's web app on a reserved box for predictable pricing, our guide to [metered versus reserved pricing](/blog/metered-vs-reserved-pricing) walks through when each model makes sense. And because the same job definition moves with the app if you later connect your own AWS account, see our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) for how those platforms handle scheduled work by comparison. ## FAQ ### What cron syntax does Ownkube support? Standard 5-field cron (minute, hour, day of month, month, day of week), plus shorthands like `@hourly` and `@daily`. The Schedule field validates your expression before it lets you save. ### Can I run a job on demand without waiting for its schedule? Yes. The Runs tab has a Run now button that fires an off-schedule run immediately, and the same action is available from the CLI as `okctl deploy job-runs trigger `. ### What happens if a job is still running when the next scheduled run is due? It depends on the Overlapping runs setting: Skip if running (the default) waits for the current run to finish, Allow overlap lets both run at once, and Replace running cancels the current run and starts the new one. ### Does a scheduled job have a public URL? No. A job deployment has no port and no public address; it runs, does its work, and exits. If you need an HTTP endpoint on a schedule or event instead, look at Ownkube's beta Functions, which support cron and interval triggers directly. ### How much does a scheduled job cost? Jobs meter the same as any other Ownkube Compute workload: CPU and memory bill per minute of actual usage against your prepaid wallet. A short-lived job that runs a few times a day costs a small fraction of your monthly plan credit. ## Where Ownkube fits If your app needs a nightly cleanup, a recurring report, or a periodic cache warm without spinning up a separate scheduler service, a scheduled job on Ownkube gets you a cron expression, retry and timeout controls, and a live Runs tab in the same dashboard as the rest of your deployments. [Deploy your first app](https://app.ownkube.io/login). --- ## Railway alternative in 2026: when to switch > When Railway stops fitting: postpaid metering, credit that does not roll over, no own-cloud path, and where Ownkube is the better trade. - Canonical: https://ownkube.io/blog/railway-alternative-2026 - Markdown: https://ownkube.io/blog/railway-alternative-2026.md - Published: 2026-09-07 - Author: Abhishek Jaiswal - Category: Engineering - Tags: railway-alternative, paas-comparison, prepaid-wallet, pricing, ownkube-compute Railway is one of the nicest ways to ship a container in 2026, and for a lot of projects it is the right answer. People start looking for an alternative not because the product got worse, but because the billing model and the roadmap stop matching what they are actually building. This post is about the specific moments that push developers off Railway, an honest table of how Railway and Ownkube compare, and where each one is the better call. **TL;DR** - [Railway bills postpaid](https://docs.railway.com/) on metered usage, so a service you forget about keeps spending against your card until you notice. - Railway plan credit is a monthly usage allowance that does not roll over, and the old generous free tier is effectively gone. - Your app runs on Railway's infrastructure only. There is no version where the same app runs in an AWS account you own. - Ownkube runs on a prepaid wallet: you load a balance, usage draws it down per minute, unused credit rolls over and never expires, and you choose metered or a flat box per app. - Railway still wins on polished developer experience and on GPU workloads. If those are your priority, stay. ## Why people look for a Railway alternative The reasons cluster into four, and they show up on the invoice or in the roadmap rather than in the demo. **The postpaid surprise bill.** Railway meters usage by the second and bills it after the fact. That is efficient when someone watches the dashboard daily. It bites when a test service runs all month, or a traffic spike meters against a card faster than an alert reaches you. The spend happens first and the bill arrives second, so the worst case is a number you did not choose. **Credit that does not roll over.** [Railway's plan includes a monthly usage allowance](https://railway.com/pricing). It is scoped to the cycle: a quiet month does not bank credit you can spend later. If you like the idea of paying in and keeping what you did not use, that is not how the model works. **The free tier people remember is gone.** Railway removed its old free plan, and what remains is a small monthly credit rather than a place to park a hobby app indefinitely. For a lot of side projects that was the reason to be there. **Your app cannot run in your own cloud.** Railway runs your workload on Railway's infrastructure, full stop. When a customer's security team asks where the app runs, or you want to keep it after you leave the platform, or you have AWS credits to burn, there is no path to your own account without a rewrite onto something else. **It is drifting upmarket.** Like most of the category, Railway is pushing toward Pro and Enterprise buyers. That is a healthy move for the company, and it quietly leaves the one-developer side project further from the center of the roadmap. ## Railway vs Ownkube The numbers below move, so read the table as the shape of each model rather than a live quote. | | Railway | Ownkube | |---|---|---| | Pricing structure | Metered usage, per second | Metered or a flat-price box, your choice per app | | Billing method | Plan credit, then postpaid to your card | Prepaid wallet the plan loads and usage drains | | Credit rollover | Monthly allowance, does not roll over | Unused wallet credit rolls over and never expires | | Entry plan | Hobby $5/mo (includes $5 usage) | Personal $5/mo (loads $5 credit), Team $20/mo | | Free option | About $1 of credit a month | Free Starter in your own AWS (you pay AWS for the instance, from about $30/mo) | | Own cloud | No | Yes, AWS today, no markup on AWS compute | | Metered rates | Per-second CPU and memory | About $18/vCPU/mo and $4.50/GiB/mo metered | | GPU | Yes | No | | At zero balance | Postpaid, no zero state | Apps pause, data kept, resume on top-up | Two different philosophies sit in that table. Railway meters everything and bills you after. Ownkube puts a prepaid wallet in front of the usage and lets you pick, per deployment, whether a workload meters or sits on a flat box. ## Where Railway genuinely wins We would rather say this plainly than pretend otherwise. **Developer experience.** Railway's onboarding, templates, and project graph are among the best in the category. If the thing you value most is how good it feels to ship the first time, Railway is hard to beat, and Ownkube's dashboard is younger. **GPU.** Railway can run GPU workloads. Ownkube cannot today. If you are doing model training or GPU inference, Ownkube is not the tool, and [Northflank](https://northflank.com/docs) is worth a look for that shape (see our [Railway, Render, and Northflank comparison](/blog/railway-vs-render-vs-northflank-2026)). If either of those is your top priority, staying on Railway is a reasonable call. ## Where Ownkube is the better trade **The bill is a balance you loaded, not a card you exposed.** On Ownkube your plan loads credit each month (Personal loads $5, Team loads $20) and usage draws it down per minute. At zero balance your apps pause and your data stays put until you top up or the next monthly load arrives. The most you can spend is the balance. A forgotten service draws the wallet down and then stops, because there is no card behind it to run up. Our [guide to bounding a cloud bill](/blog/prepaid-wallet-no-surprise-bill) walks through the setup. **Unused credit rolls over and never expires.** Whatever you do not spend in a month stays on your balance, and the next month loads on top of it. A quiet month is credit banked, not money burned. The one thing that resets monthly is the build-minutes allowance (300 on Personal, 1,500 on Team), not the wallet dollars. **Metered or flat, per app.** Run a bursty web app metered so an idle hour costs almost nothing, and put a steady worker on a flat Spark box (from $5/mo) or Core box (from $58/mo) so its price never moves, in the same project, and switch either later. Metered bills both CPU and memory on actual per-minute usage, and personal software tends to idle, so the CPU line goes to almost nothing while nothing is happening. **The same app can move into your own AWS with no rewrite.** You ship on Ownkube Compute with no cloud account. Later, if a customer requires it or you want your own credits to apply, the same app moves into an AWS account you own, and its logs, metrics, and health checks come with it. Starter is free on one AWS instance (you pay AWS for the instance); Production is $5 per vCPU plus $1 per GiB of RAM with no markup on AWS compute and AWS Activate credits applied in full. Our [Fly.io alternative post](/blog/fly-io-alternative-own-aws) covers that path in depth. ## FAQ ### Is there a Railway free tier? Not in the way there used to be. Railway removed its old free plan; what remains is a small monthly credit (about a dollar) on the Hobby plan rather than an indefinite free place to park an app. If you want a genuinely free option, Ownkube's Starter runs free in your own AWS account (you still pay AWS for the underlying instance, from about $30/mo). ### Does Railway credit expire? Railway's plan credit is a monthly usage allowance scoped to the billing cycle, so it does not roll over: a quiet month does not bank credit for later. Ownkube's prepaid wallet works the other way. Unused credit rolls over to the next month and never expires. We cover the difference in detail in [does Railway credit roll over or expire](/blog/does-railway-credit-expire). ### Can Railway run in my own cloud? No. Railway runs your app on Railway's own infrastructure, and there is no option to run the same app in an AWS account you control. Ownkube runs on managed compute by default and lets the same app move into your own AWS with no rewrite when you need it. ### Railway vs Ownkube cost: which is cheaper? Price the whole app, not the web process. On compute alone the two are close: Railway's per-second CPU lands near Ownkube's metered CPU, and Ownkube's memory rate is lower. The gap opens once you add the database and the cache that every real app needs. Railway bills managed Postgres and Redis as separate metered services with a markup; Ownkube runs them on near-cost boxes (a database box from $4 a month plus $0.25 per GB of storage, a Valkey cache box from $2). Take a small always-on stack, a 1 vCPU / 1 GiB web app, a 1 GiB / 10 GB Postgres, and a small cache: that lands around $44 to $56 a month on Railway against roughly $27 on Ownkube. Treat those as illustrative (Railway's metered lines move with load, as of October 2026), and run your own sizes on the [homepage calculator](/#pricing-compare). For a busy, always-hot web app the compute gap narrows, but the bundled database and cache keep the full-stack total lower. **Reference:** For the side-by-side breakdown, see [Ownkube vs Railway](/docs/compare/railway) in the docs. ## Where Ownkube fits Move off Railway when the postpaid meter, the credit that does not roll over, or the lack of an own-cloud path is the thing costing you, and stay if polished developer experience or GPU is what you need most. Ownkube is built for personal software and small teams: ship in seconds on Ownkube Compute with no cloud account, load a wallet instead of exposing a card, keep the credit you do not spend, and take the same app into your own AWS when you are ready. [Deploy your first app](https://app.ownkube.io/login). --- ## Preview environments for every pull request > Give every branch its own live, TLS-secured environment on Ownkube, plus what's manual today versus what native per-PR previews will automate. - Canonical: https://ownkube.io/blog/preview-environments-per-pr - Markdown: https://ownkube.io/blog/preview-environments-per-pr.md - Published: 2026-09-06 - Author: Abhishek Jaiswal - Category: How-To - Tags: preview-environments, pull-requests, ci-cd, tutorial, ownkube-compute A preview environment is a live, disposable copy of your app that runs the code on a branch instead of main, so a reviewer or a designer can click around a pull request instead of reading a diff. Ownkube does not yet spin one up automatically the moment you open a PR (that automation is on the roadmap), but the pieces it does ship, a deployment per branch, an environment per stage, and a public hostname with automatic TLS [in under a minute](/blog/deploy-a-web-app-in-under-a-minute), get you a working preview today with a few clicks instead of a platform team. This guide covers what's shipped, how to set up a per-branch preview environment on Ownkube Compute, and how to tear it down cleanly when the branch merges. **TL;DR** - Ownkube doesn't auto-create or auto-delete a preview environment when a pull request opens or closes yet. That's listed on the roadmap as native per-PR isolation with auto-teardown. - What is shipped: an **environment** per stage or branch, a **deployment** that builds from a specific branch on git push, and an auto-allocated `*.ownkube.app` hostname with automatic TLS for every deployment. - You create the branch's environment and deployment once, from the dashboard, and Ownkube rebuilds it automatically on every push to that branch after that. - **Health checks** on the deployment's Settings tab keep the preview's URL from serving traffic until the container actually passes its readiness probe, so reviewers never hit a half-started app. - Delete the environment when the PR merges or closes. Environment-level shared variables aren't available yet, so set variables per deployment, not once per environment. ## What "preview environment" means on Ownkube today Ask for "preview environments" and most people mean one specific thing: open a pull request, get a URL automatically; close it, and the URL and its resources disappear automatically. That exact loop, triggered by the pull request lifecycle itself, isn't wired up on Ownkube yet. It's real work on the roadmap (per-PR isolation with auto-teardown), and we'd rather tell you that plainly than have you go looking for a webhook setting that doesn't exist. What is wired up, and generally available today, is the building block underneath that loop: an **environment** for each stage of your app, and a **deployment** inside it that rebuilds every time you push to a chosen branch. Multi-environment promotion lets you move a build from one environment to the next once it's reviewed. Put those together and a "preview environment" is a couple of dashboard clicks: a new environment named after the branch, a deployment pointed at that branch, and a live `*.ownkube.app` URL with TLS already on it. It isn't automatic yet, but it's fast, and it's real infrastructure, not a shared staging box everyone fights over. ## What you need before you start - A GitHub or GitLab repository connected to your Ownkube organization (GitHub App install, or a GitLab account connection). - A repository that either has a [Dockerfile](https://docs.docker.com/) or is in a language Ownkube's automatic build can detect (most common Node, Python, Go, and Ruby setups build with no Dockerfile at all). - The branch you want to preview, already pushed. ## Step 1: create an environment for the branch Environments are how Ownkube groups deployments into a stage: production, staging, and now, a branch preview. From your organization's dashboard, open **Settings**, go to the **Environments** tab, and create a new one named after the branch (`preview-checkout-redesign` reads better six weeks from now than `env-3`). You don't need a naming convention Ownkube enforces, but a consistent one saves you from guessing which environment belongs to which stale PR when you're cleaning up later. ## Step 2: point a deployment at the branch Inside the new environment, create a deployment from your connected repository, and pick the branch instead of the default one. ```text Deploy new -> Web service Source: your-org/your-repo Branch: checkout-redesign Build target: Dockerfile detected (or Auto, if the repo has none) Environment: preview-checkout-redesign ``` If the repo has a Dockerfile, Ownkube builds from it (and lets you pick which one if there's more than one). If it doesn't, Ownkube detects the language and builds automatically, no Dockerfile required either way. Set the port your app listens on and any environment variables the branch needs. Variables are per-deployment today, there's no environment-level shared variable set yet, so if the preview needs the same secrets as staging, copy them over once when you create the deployment. ## Step 3: confirm the health check before you share the link Every deployment has a [**Health checks**](/docs) section on its Settings tab (startup, liveness, and readiness probes). Point it at a real endpoint your app already exposes, `/health` or `/` is fine for most services, so Ownkube knows the container is actually ready before it lets traffic hit the public URL. ```text Health check path: /healthz Initial delay: 5s Timeout: 3s ``` Skip this and the auto-allocated hostname can resolve before your app has finished starting, which is exactly the kind of flaky first impression a preview environment is supposed to avoid. ## Step 4: share the URL Once the deployment's status reads healthy, its **Overview** tab shows the public hostname, an auto-allocated `*.ownkube.app` domain with automatic TLS already issued. Drop that link in the pull request description or a comment. Every subsequent push to the branch rebuilds the same deployment and the URL doesn't change, so you only need to post it once per PR. ```bash # Verify a preview deployment is live and healthy from the CLI okctl deploy status -o json ``` ## Step 5: tear the environment down when the PR closes This is the manual half, today. When the branch merges or the PR closes, delete the deployment (or the whole environment, if you made one per branch) from the dashboard. Nothing sensitive is left running: a deleted deployment stops billing and releases its hostname immediately. Until native auto-teardown ships, the honest answer is to treat this the way you'd treat closing a stale browser tab: a habit, not automation. If your team opens a lot of pull requests, keeping preview environment names prefixed and sorted makes the monthly cleanup pass fast. ## When to just use staging instead Not every branch needs its own environment. A one-line copy fix doesn't need a full preview; a checkout flow rewrite does. If your team finds itself creating a preview for nearly every PR, that's also a signal you're a good candidate to hear about native per-PR previews first once they ship, keep an eye on the [changelog](https://ownkube.io/changelog). For teams that outgrow a single shared staging environment before automated previews land, our [internal developer platform guide](/blog/internal-developer-platform-2026) covers the broader pattern of self-service environments without a platform team building it from scratch. ## FAQ ### Does Ownkube create a preview environment automatically when I open a pull request? Not yet. That's on the roadmap as native per-PR isolation with automatic teardown. Today you create the environment and deployment once, and Ownkube rebuilds it automatically on every push to the branch after that. ### Does a preview environment get its own URL and TLS certificate? Yes. Every deployment, including one on a branch, gets an auto-allocated `*.ownkube.app` hostname with automatic TLS. That part is fully automatic; only the create-and-delete step around it is manual today. ### Can I set an environment variable once and have every preview inherit it? Not yet. Environment-level shared variables exist in the data model but there's no UI to set them, so variables are per-deployment. Copy what a preview needs when you create it. ### What happens to a preview environment's data when I delete it? Deleting the deployment removes it and releases its hostname. If the preview included its own database or cache, that data goes with it, so don't point a branch preview at production data. ### Is there a cost to running a preview environment? Yes, a preview deployment draws down your [prepaid wallet](/blog/prepaid-wallet-no-surprise-bill) like any other deployment, metered per minute by default so an idle preview between review rounds barely registers. Delete it once the PR is done rather than letting it run indefinitely. **Reference:** For the full definition, see [Preview Environment](/docs/glossary/preview-environment) in the docs glossary. ## Where Ownkube fits If you want the exact "PR opens, URL appears, PR closes, it's gone" loop, that's a roadmap item, not something to fake here. What you can build today is close: a real environment and a real deployment per branch, live in about a minute, with a working TLS URL and a health check protecting it, torn down with one click when you're done. For a team that's tired of a single shared staging box, that's a meaningful upgrade even before the last mile automates. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to keep your cloud bill from surprising you > How to bound what you spend on a deploy platform: how a prepaid wallet works, why rollover credit changes the math, and how to set it up. - Canonical: https://ownkube.io/blog/prepaid-wallet-no-surprise-bill - Markdown: https://ownkube.io/blog/prepaid-wallet-no-surprise-bill.md - Published: 2026-09-05 - Author: Abhishek Jaiswal - Category: How-To - Tags: cloud-cost, prepaid-wallet, pricing, railway-alternative, ownkube-compute The surprise cloud bill is a genre of story at this point: a service you forgot about ran all month, or a traffic spike metered against a card, and the invoice showed up after the money was already gone. The fix is not watching a dashboard more carefully. It is putting a ceiling on the spend before anything runs, so the worst case is a number you chose on purpose. This guide walks through how a prepaid wallet does that, where the usual metered and fixed-instance models leak, and how to set your own limit on Ownkube in a few minutes. **TL;DR** - Postpaid billing ([Railway](https://docs.railway.com/), [Render](https://render.com/docs), most clouds) bills you after the meter runs, so a forgotten service or a spike lands on a card you were not watching. - A prepaid wallet flips the order: you load a balance, usage draws it down per minute, and at zero your apps pause with their data kept. The most you can lose is the balance. - On Ownkube, your plan loads the wallet each month (Personal loads $5, Team loads $20), and unused credit rolls over and never expires. - You still control per-app spend with CPU and memory limits, and you can pick metered or a flat box per deployment. - Set it up by picking a plan, setting a top-up ceiling, and putting a limit on each workload. ## Why postpaid billing surprises you Most deploy platforms are postpaid. The meter runs, or the instance exists, and the invoice arrives at the end of the cycle. That model is fine when someone is watching it every day. It bites in three predictable ways. A metered platform like Railway bills per second for what runs, so a service you spun up for a test and forgot keeps spending until you notice. A fixed-instance platform like [Render](https://render.com/pricing) hides the same problem in a quieter shape: you pay for the instance whether it is busy or idle, so the waste is steady instead of sudden. And any postpaid model exposes you to the spike, where a busy day or a runaway process meters against your card faster than an alert can reach you. None of these is a pricing bug. They are all the same structural fact: the spend happens first and the bill comes second. If you want the bill to be bounded, you have to change the order. ## How a prepaid wallet changes the order A prepaid wallet puts the money in front of the usage. You keep a balance of credit. Everything you run draws that balance down per minute for what it actually uses. When the balance reaches zero, your apps pause and your data stays put until you add credit or the next monthly load arrives. The practical consequence is that the most you can spend is the balance you loaded. A forgotten service cannot run up a four-figure bill, because there is no card behind it to charge. It draws down the wallet and then stops. You decide the ceiling once, up front, instead of reconciling it after the fact. On Ownkube the wallet is the billing model for Ownkube Compute, the managed compute you deploy onto with no cloud account of your own. Two things make the math friendlier than a plain prepaid balance: First, **unused credit rolls over and never expires.** Your plan loads credit each month and usage drains it, but whatever you did not spend stays on your balance and the next month loads on top of it. A quiet month is not money burned. This is the part most usage-based platforms get wrong: on a postpaid meter there is nothing to roll over, and on many prepaid systems the credit expires at the cycle boundary. Here the dollar balance persists. Second, **an idle app barely draws the wallet down.** Compute meters both CPU and memory on actual per-minute usage, and most personal software sits idle most of the day. The CPU line goes to almost nothing while nothing is happening, so the balance you loaded stretches further than a fixed instance would let it. One caveat to be straight about: the monthly build-minutes allowance (300 minutes on Personal, 1,500 on Team) resets each month and does not roll over. It is the wallet dollar balance that carries forward, not the build allowance. ## Set your ceiling on Ownkube Here is the setup, start to finish. ### 1. Pick how the wallet gets funded You can skip the plan and just top up, or pick a plan that loads credit into your wallet each month. | Plan | Monthly price | Loads into wallet | Build minutes | |---|---|---|---| | Pay as you go | none | top up from $5 | billed per minute | | Personal | $5 / month | $5 / month | 300 / month | | Team | $20 / month | $20 / month | 1,500 / month | All three draw from the same prepaid wallet. Personal adds more services, every database size, and database backups. Team adds unlimited organizations, shared billing, and priority build capacity. There is no free tier on Compute: whatever you load, by top-up or plan, is the ceiling of what you spend, and it is also credit you get to use. ### 2. Decide how each app is priced Metered is the default and fits most apps. You set a CPU limit and a memory limit as ceilings, and the bill follows the meter, not the size you picked. An idle app costs almost nothing on CPU. If you would rather a workload have a flat, predictable price, [put it on a reserved box instead](/blog/metered-vs-reserved-pricing): a fixed-size Spark box (shared CPU, from $5 a month) or Core box (dedicated CPU, from $58 a month) that bills the same rate busy or idle. You choose per deployment and can switch later, so a bursty web app can meter while a steady worker sits on a flat box, in the same project. Databases and caches are always reserved. A managed Postgres runs on a database box from $4 a month plus storage at $0.25 per GB. A Valkey cache runs on a cache box from $2 a month. ### 3. Set a per-workload limit The CPU and memory limits you set on each deployment are the ceilings that cap how fast any single app can draw the wallet down. Set them to what the app actually needs plus headroom. A limit is not a reservation, so on metered pricing you are not billed for the ceiling, only for what the app uses up to it. ```bash # Deploy with explicit resource ceilings via the CLI okctl deploy --cpu-limit 500m --memory-limit 512Mi ``` ### 4. Top up on your terms If usage runs past the loaded credit before the next cycle, you top up the wallet for any amount from a $5 minimum, any time. Because top-ups are something you do on purpose, there is no automatic charge racing ahead of you. The rates behind the meter are about $18 per vCPU per month and $4.50 per GiB per month, which puts a typical always-on app roughly a third under the equivalent [Railway bill](https://railway.com/pricing). ## When a fixed box is the safer ceiling Metered is not always the calmest choice. A workload that runs hot around the clock, like a busy worker that never idles, will meter continuously, and for that shape a flat reserved box gives you a price that never moves. The rule of thumb: meter the things that idle (most web apps and side projects), and reserve the things that run steadily. Since you pick per deployment, you do not have to choose one model for everything. If predictability across the whole platform matters more than squeezing the idle hours, our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) walks through how each billing model behaves at the end of the month. ## FAQ ### Does prepaid credit on Ownkube expire? No. Unused wallet credit rolls over to the next month and never expires. The dollar balance persists across cycles, and each monthly plan load adds on top of it. The one thing that resets monthly is the build-minutes allowance, not the wallet balance. ### What happens when my balance hits zero? Your apps pause and your data is kept. Nothing is deleted. Adding credit with a top-up, or the next monthly plan load, resumes them. ### Is there a free tier on Ownkube Compute? No, but you don't need a subscription either: top up from $5 and pay per minute. The Personal plan is $5 a month and loads $5 of credit you then spend, so the plan price is also usable balance. If you want a genuinely free option, running in your own AWS account on a Starter instance is free (you pay AWS for the instance). ### How is this different from Railway's usage-based billing? Railway is postpaid: the meter runs and the invoice comes after, and [unused amounts do not roll over](/blog/does-railway-credit-expire). Ownkube is prepaid: you load a balance, usage draws it down, and leftover credit carries forward. The ceiling on what you can spend is the balance, not a card. ### Can I still get a predictable flat monthly price? Yes. Put the deployment on a reserved Spark or Core box and it bills a flat rate for as long as it exists, busy or idle. You choose metered or reserved per deployment. **Reference:** For the full definition, see [Prepaid Wallet](/docs/glossary/prepaid-wallet) in the docs glossary. ## Where Ownkube fits If the surprise invoice is the thing you actually want gone, the wallet is the answer: you load a balance, usage draws it down, leftover credit rolls over, and the worst case is a number you set. Add per-workload limits and the choice of metered or a flat box, and the spend stays inside lines you drew on purpose. It is built for personal software and small teams who would rather bound the bill than reconcile it. [Deploy your first app](https://app.ownkube.io/login). --- ## Prepaid cloud wallet vs usage-based billing > How a prepaid cloud wallet and postpaid usage billing each charge, why rollover credit that never expires changes the math, and when a meter wins. - Canonical: https://ownkube.io/blog/prepaid-wallet-vs-usage-based-billing - Markdown: https://ownkube.io/blog/prepaid-wallet-vs-usage-based-billing.md - Published: 2026-09-05 - Author: Abhishek Jaiswal - Category: Product - Tags: prepaid-wallet, usage-based-billing, cloud-cost, pricing, railway-alternative A prepaid cloud wallet is a credit balance you load up front, that your apps then draw down per minute as they run. Usage-based billing does the opposite: the meter runs first and the invoice arrives after. Both charge you for what you use. The difference is the order, and the order is what decides whether a forgotten service can surprise you. [Railway has argued](https://docs.railway.com/) that usage-based pricing is the honest model, because you pay for exactly what you consume and nothing more. That is a fair point, and it is worth taking seriously. This post is the honest counter: a prepaid wallet charges for exactly what you consume too, and it does one more thing a postpaid meter cannot, which is bound the total before anything runs. **TL;DR** - A prepaid wallet is a loaded balance. Usage draws it down per minute, and at zero your apps pause with their data kept, so the most you can spend is the balance. - Usage-based (postpaid) billing meters what you run and charges a card after the cycle. It is precise, but the total is unbounded until the invoice lands. - On Ownkube, unused wallet credit rolls over and never expires. A quiet month carries forward instead of resetting to zero. - A prepaid wallet still meters real usage under the hood, so an idle app barely draws it down. You get precision and a ceiling. - A pure usage-based meter still makes sense for spiky, high-scale, or pass-through-billed workloads. We say where below. ## What a prepaid cloud wallet actually is A prepaid wallet is a dollar balance you keep on the platform. You load it (or your plan loads it for you), and every app you run draws it down per minute for the CPU and memory it actually uses. It is the same model as a transit card or a prepaid SIM: the money goes in first, and consumption spends it down. The mechanics that matter are what happens at the edges. When the balance runs low, you top up. When it reaches zero, your apps pause and your data stays put until you add credit. There is no card being charged in the background, so there is no total that can run past the balance you chose. The ceiling is not an alert or a budget you hope holds. It is the money itself. On Ownkube the wallet is the billing model for Ownkube Compute, the managed compute you deploy onto with no cloud account of your own, no config, and no subscription to start. You can fund the wallet with a top-up from $5, or pick a plan that loads it each month: Personal is $5 a month and loads $5 of credit, Team is $20 a month and loads $20. Usage draws that balance down, and you top up any time from a $5 minimum. ## What usage-based (postpaid) billing is, and where it leaks Usage-based billing meters consumption and charges you after the fact. [Railway bills per second](https://railway.com/pricing) for what runs. Most clouds bill per hour or per GB at the end of a cycle. The pitch is precision, and the pitch is real: you are charged for what you used and not a flat box you half-filled. The leak is not in the precision. It is in the timing. Because the spend happens before the bill, three shapes of surprise are structural, not bugs: - **The forgotten service.** You spin something up for a test, move on, and it meters quietly against your card until the invoice reminds you it existed. - **The spike.** A busy day or a runaway process meters faster than any alert can reach you, and the overage is already spent by the time you read the email. - **No ceiling.** A budget cap on a postpaid meter is a notification, not a wall. The meter can cross it, because the thing enforcing the limit is a promise to email you, not the absence of a payment method. None of this makes usage-based billing dishonest. It makes it unbounded. Honesty about the rate and boundedness of the total are two different properties, and a postpaid meter only gives you the first. ## Why rollover credit that never expires changes the math The strongest objection to prepaid is that it feels like a loss. If you load $20 and use $12, did you just burn $8? On many prepaid systems, yes, because the credit expires at the cycle boundary. That is the version of prepaid worth being suspicious of. Ownkube does not do that. Unused wallet credit rolls over and never expires. Your plan loads credit each month, usage drains it, and whatever you did not spend stays on your balance while the next month loads on top. A quiet month is money still in your account, not money gone. Over a year of uneven usage, the balance self-levels: the light months bank credit that the heavy months draw on. This is the piece a postpaid meter cannot match, because there is nothing to roll over. On a pure meter you pay for August in September and the slate is blank again. On a prepaid wallet with rollover, a $5 month you barely touched leaves roughly $5 waiting for the month you ship hard. The rate you pay per vCPU is the same either way. What changes is that underuse stops being a penalty. One honest caveat: the monthly build-minutes allowance (300 minutes on Personal, 1,500 on Team) resets each month and does not roll over. It is the dollar balance that carries forward, not the build allowance. We would rather say that plainly than let you assume everything rolls. ## Prepaid vs usage-based billing, side by side Both bill for real usage. The table is about what happens around that fact. | | Prepaid wallet (Ownkube) | Usage-based / postpaid (Railway, most clouds) | |---|---|---| | Order of events | Load balance, then usage draws it down | Usage runs, then a card is charged | | Charges for real usage | Yes, per minute on CPU and memory | Yes, per second or per hour | | Total spend ceiling | The balance you loaded | Unbounded until the invoice | | Idle app | Barely draws the wallet down | Metered continuously if the instance exists | | Unused amount | Rolls over, never expires | Nothing to roll over | | At zero / non-payment | Apps pause, data kept | Meter keeps running against the card | | Forgotten service | Draws down the wallet, then stops | Runs up the bill until you notice | | Best for | Personal software, small teams, bounded budgets | Spiky scale, pass-through billing, pooled org spend | ## When a pure usage-based meter still makes sense A prepaid wallet is not the right tool for every shape of spend, and it would be dishonest to pretend otherwise. - **Genuinely spiky, high-scale traffic.** If your load swings by orders of magnitude and you need to absorb every spike without ever pausing, a postpaid meter that just bills the overage fits better than a balance you have to keep ahead of. A prepaid ceiling is a feature when you want a limit and a friction when you never do. - **Pass-through or client-billed usage.** Agencies and platforms that re-bill consumption to customers often want the raw postpaid meter to reconcile against invoices, not a pooled prepaid balance. - **Large orgs with central procurement.** When finance already runs on monthly postpaid invoices and pooled commitments, a per-project prepaid wallet can be more accounting than it is worth. The honest framing is that usage-based billing optimizes for never interrupting the workload, and a prepaid wallet optimizes for never being surprised by the bill. Most personal software and small-team apps want the second. Some high-scale, re-billed, or procurement-heavy setups want the first. Ownkube leads with the wallet because that is who Ownkube Compute is for: a cloud for personal software. For a closer look at how the wallet mechanics play out in day-to-day usage, see [how to keep your cloud bill from surprising you](/blog/prepaid-wallet-no-surprise-bill). For the reserved-vs-metered choice inside the wallet, see [metered or a flat box: how to choose pricing for each app](/blog/metered-vs-reserved-pricing). And for where this lands against the field, [our comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) walks through each billing model at the end of the month. ## FAQ ### Is a prepaid wallet just usage-based billing with extra steps? Under the hood it meters the same real usage per minute, so the rate you pay is the same idea. The extra step is loading the balance first, and that step is the whole point: it caps the total at what you loaded, which a postpaid meter cannot do. ### Does prepaid credit on Ownkube expire? No. Unused wallet credit rolls over to the next month and never expires. The dollar balance persists across cycles and each monthly plan load adds on top. The one thing that resets monthly is the build-minutes allowance, not the wallet. ### Is prepaid more expensive than paying only for what I use? No. You still pay per minute for actual CPU and memory usage, so an idle app barely registers. The metered rates are about $18 per vCPU per month and $4.50 per GiB per month, roughly a third under an equivalent Railway bill for a typical always-on app. ### What happens if I load $20 and only use $12? The remaining $8 stays on your balance and carries into next month, when your plan loads more on top. Underuse banks credit rather than burning it. ### When would usage-based billing be the better fit? When you have genuinely spiky high-scale traffic you never want to pause, when you re-bill consumption to clients and need the raw meter to reconcile, or when a large org already runs on pooled monthly postpaid invoices. ## Where Ownkube fits A prepaid wallet and a usage-based meter both charge for what you use. The wallet adds a ceiling you set on purpose and rollover credit that never expires, so a quiet month carries forward and a forgotten service cannot outrun the balance. Usage-based billing is the honest choice when never interrupting the workload matters more than bounding the bill, and we say so. For personal software and small teams who would rather draw the line before the spend than reconcile it after, the wallet is the calmer default, and it is the model behind Ownkube Compute. [Deploy your first app](https://app.ownkube.io/login). --- ## Northflank alternative for solo and small teams > Northflank is powerful but keeps Kubernetes in front of you and targets platform teams. An honest comparison for a solo developer who wants to ship. - Canonical: https://ownkube.io/blog/northflank-alternative-solo-devs - Markdown: https://ownkube.io/blog/northflank-alternative-solo-devs.md - Published: 2026-09-04 - Author: Abhishek Jaiswal - Category: Engineering - Tags: northflank-alternative, paas-comparison, kubernetes, developer-platform, ownkube-compute Northflank is one of the most capable platforms in the category, and if you are a platform team standing up infrastructure, it is a serious tool. The reason a solo developer or a team of three goes looking for an alternative is not that Northflank is weak. It is that Northflank keeps Kubernetes in view and is built for people whose job is running infrastructure, when you just want to push an app and get a URL. This post is an honest comparison for that reader. **TL;DR** - [Northflank](https://northflank.com/docs) is powerful and cheap on raw CPU, and it has real multi-cloud bring-your-own-cloud today. - Its own tagline is "making Kubernetes boring," which tells you the truth: Kubernetes is still in front of you, and the product is aimed at platform teams. - Ownkube is built for one developer or a small team: deploy in seconds, no cluster to reason about, a prepaid wallet so nothing surprises you. - Northflank genuinely wins on raw per-vCPU rate, multi-cloud, and GPU today. We would rather say that than pretend. - If you want power and a platform team to run it, pick Northflank. If you want to ship personal software without becoming your own platform team, pick Ownkube. ## Why a solo developer looks past Northflank **Kubernetes is still in the room.** Northflank's pitch is that it makes Kubernetes boring, and it does a good job of that. But the concepts are still present: you are reasoning about a cluster, about how workloads schedule onto it, about the machinery underneath. For a platform team that is the point. For one person shipping a side project, it is surface area you did not want to own. **It is built for platform teams.** Northflank's features and its own-cloud story assume an org that is standing up infrastructure for other engineers. The defaults, the docs, and the roadmap all point at that buyer. A solo developer is not the center of gravity. **Setup is more than "push and get a URL."** The power comes with configuration. When your actual requirement is "deploy this container in seconds and never think about the substrate," the extra knobs are cost, not benefit. None of this is a knock on Northflank. It is a different buyer. The question is which buyer you are. ## Northflank vs Ownkube Prices move, so read the table as the shape of each model rather than a live quote. | | Northflank | Ownkube | |---|---|---| | Built for | Platform teams standing up infrastructure | One developer or a small team shipping personal software | | Kubernetes exposure | Present, made "boring" but visible | None to reason about; deploy in seconds | | Pricing structure | Metered per vCPU-hour (fixed plans optional) | Metered or a flat-price box, your choice per app | | Billing method | Postpaid pay-as-you-go | Prepaid wallet the plan loads and usage drains | | Credit rollover | Postpaid, nothing to roll over | Unused wallet credit rolls over and never expires | | Raw CPU rate | Cheapest of the group per vCPU | About $18/vCPU/mo metered (memory rate is where we win) | | Own cloud | Yes, multi-cloud, no added platform cost in your VPC | Yes, AWS today (GCP and Azure coming soon), no markup on AWS compute | | GPU | Yes | No | | Entry point | Sandbox free, then pay-as-you-go | Personal $5/mo (loads $5 credit), Team $20/mo | Two different centers of gravity sit in that table. Northflank hands a powerful, multi-cloud, Kubernetes-based platform to a team that will operate it. Ownkube hides the substrate entirely so one person can ship without operating anything. ## Where Northflank genuinely wins **Raw per-vCPU rate.** [Northflank's metered CPU](https://northflank.com/pricing) works out cheaper than ours per vCPU. If the single number you optimize for is dollars per vCPU-hour, Northflank wins that line, and we would rather say so. **Multi-cloud own cloud, today.** Northflank runs in AWS, GCP, and other clouds inside your own account now. Ownkube's own-cloud path is AWS today, with GCP and Azure coming soon. If you need GCP or multi-cloud in your own account right now, Northflank is the honest answer. **GPU.** Northflank can run GPU workloads. Ownkube cannot today. Model training or GPU inference is a reason to choose Northflank. ## Where Ownkube is the better trade for a solo developer **Deploy in seconds, no cluster to reason about.** Ownkube Compute takes a git push or a container and gives you a [running URL with automatic TLS](/blog/deploy-a-web-app-in-under-a-minute), no cloud account, no config, no subscription to start. There is no cluster in your mental model. That is the whole product for someone who wants to ship, not operate. **A prepaid wallet instead of a postpaid meter.** Northflank is postpaid pay-as-you-go, which is the model behind every "I left something running and got a surprise bill" story. Ownkube loads a wallet each month (Personal loads $5, Team loads $20), usage draws it down per minute, and at zero balance apps pause with data kept. The most you can spend is the balance you loaded. Unused credit rolls over and never expires. Our [guide to bounding a cloud bill](/blog/prepaid-wallet-no-surprise-bill) covers the setup. **Metered or flat, per app.** Where Northflank meters, Ownkube lets you choose per deployment: meter a bursty web app so idle hours cost almost nothing, or put a steady worker on a flat Spark box (from $5/mo) or Core box (from $58/mo). Managed Postgres (from $4/mo plus $0.25 per GB storage) and a Valkey cache (from $2/mo) are always a flat box. **Memory is where the money is.** Our per-GiB memory rate is well under the metered competition, and personal software tends to be memory-heavy and CPU-idle, so the app sits on the memory line most of the day. That is why a typical small always-on app can total less than the raw-CPU comparison suggests, even when the per-vCPU number favors Northflank. **The same app moves into your own AWS with no rewrite.** Ship on managed compute, then take the same app into an AWS account you own when a customer requires it or you want your credits to apply, with logs, metrics, and health checks coming along. Starter is free on one AWS instance (you pay AWS for the instance); Production is $5 per vCPU plus $1 per GiB RAM with no markup on AWS compute. See our [Railway, Render, and Northflank comparison](/blog/railway-vs-render-vs-northflank-2026) for how the own-cloud stories differ. ## FAQ ### Is Northflank good for solo developers? It can work, but it is built for platform teams, and Kubernetes stays in view. If your requirement is "deploy this in seconds and never think about a cluster," a developer platform like Ownkube fits that better. If you want the power and have someone to operate it, Northflank is strong. ### Is Ownkube cheaper than Northflank? On raw per-vCPU rate, no, Northflank's metered CPU is cheaper, and it is the closest competitor we have on price. The answer changes when you price the whole app instead of the web process. A real app needs a managed Postgres and usually a cache, and Northflank bills both as separate services on top of compute. Ownkube runs them on near-cost boxes (a database box from $4 a month plus $0.25 per GB of storage, a Valkey cache box from $2). Take a small always-on stack, a 1 vCPU / 1 GiB web app, a 1 GiB / 10 GB Postgres, and a small cache: that lands around $37 a month on Northflank against roughly $27 on Ownkube. The figures are illustrative, mapped to each provider's nearest real tier as of October 2026, and a CPU-hot app narrows the compute line. The full-stack total stays lower on Ownkube because the database and cache are bundled near cost, not because of a per-vCPU trick. Run your own sizes on the [homepage calculator](/#pricing-compare). ### Does Ownkube use Kubernetes? Ownkube runs on managed compute, and the point of the product is that you never reason about a cluster: you deploy an app and get a URL. Where Northflank makes Kubernetes "boring" but visible, Ownkube keeps the substrate out of your way entirely. ### Can Ownkube run in my own cloud like Northflank? Yes, on AWS today, with GCP and Azure coming soon. The same app you ship on Ownkube Compute moves into an AWS account you own with no rewrite, and there is no markup on AWS compute. Northflank's own-cloud is multi-cloud today and aimed at platform teams; Ownkube's is aimed at one developer. ### Does Ownkube support GPU? Not today. If you need GPU training or inference, Northflank is the better choice for that workload. ## Where Ownkube fits Pick Northflank if you are a platform team that wants power, multi-cloud in your own account, or GPU, and has someone to operate it. Pick Ownkube if you are one developer or a small team who wants to deploy in seconds with no cluster to reason about, a wallet that bounds the bill and keeps the credit you do not spend, and the option to take the same app into your own AWS later with no rewrite. [Deploy your first app](https://app.ownkube.io/login). --- ## Move the same app into your own AWS account > Connect your AWS account to Ownkube, pick Starter or Production, and redeploy the exact app you already built, with its logs, metrics, and health checks intact. - Canonical: https://ownkube.io/blog/move-app-to-your-own-aws - Markdown: https://ownkube.io/blog/move-app-to-your-own-aws.md - Published: 2026-09-03 - Author: Abhishek Jaiswal - Category: How-To - Tags: own-cloud, aws, portability, migration, ownkube-compute An app you built and deployed on Ownkube Compute can move into your own AWS account, or a customer's, without touching the code or the Dockerfile. This is what changes, what stays the same, and the steps to do it. **TL;DR** - Ownkube Compute (no cloud account, deploy in seconds) and running in your own AWS account are two on-ramps for the same platform. You can switch a project from one to the other. - Connecting an AWS account uses a CloudFormation stack that creates a least-privilege IAM role. Ownkube never receives your AWS keys. - Two cluster shapes: Starter is free forever on a single AWS instance (you pay AWS directly, [roughly $30/month](https://aws.amazon.com/ec2/pricing/) for the instance itself); Production is multi-AZ at $5 per vCPU per month plus $1 per GiB of RAM per month, metered per second, with no markup on the AWS bill. - AWS Activate and other startup credits apply in full to whatever you pay AWS for either shape. - Your app redeploys the same way (git push or a Dockerfile), and its logs, metrics, and health checks show up in the same dashboard you already use. GCP and Azure are coming soon, not available yet. ## Why this matters more than it sounds Most platforms make you choose upfront: managed compute you don't operate, or your own cloud account you manage yourself. Picking wrong means a rewrite later, new environment variables, a different deploy pipeline, and a second dashboard to learn for logs and metrics. Ownkube treats that as one decision you can revisit. [Build and ship fast on Ownkube Compute](/blog/deploy-a-web-app-in-under-a-minute) with no AWS account of your own. If the app grows into something you want on your own infrastructure, for compliance, cost control at scale, or because you're standing it up for a customer, you connect an AWS account and deploy the same app there. The dashboard, the deploy flow, and the observability are the same surface either way. ## What "no rewrite" actually means The app doesn't know which world it's running in. If you deploy from a [Dockerfile](https://docs.docker.com/) or a git push today, that's still how you deploy after moving to your own AWS account. Environment variables and the deployment's settings carry over the same way. Moving to a Starter (K3s) cluster in your own AWS account also unlocks custom domains, which are a Starter feature today and on the roadmap for Compute. What changes is who owns the underlying compute and who you pay for it: on Compute, you pay Ownkube through the [prepaid wallet](/blog/prepaid-wallet-no-surprise-bill); in your own AWS account, you pay AWS directly for the instance, EKS control plane, and storage, and Ownkube only ever operates the platform on top of it. The other half of "no rewrite" is observability. Logs, metrics, and health checks aren't something you have to wire up again on the new infrastructure. They're part of the same platform layer, so they follow the deployment into your AWS account instead of resetting to zero. ## Step 1: Connect your AWS account Connecting doesn't hand Ownkube your AWS credentials. It runs a CloudFormation stack in your account that creates a least-privilege IAM role scoped to what the platform needs to provision and manage a cluster, nothing broader. The fastest way is the CLI: ```bash okctl aws connect ``` By default this opens a browser handoff to run the CloudFormation stack in the AWS console. If you already have AWS credentials in your shell, you can run it end to end without leaving the terminal: ```bash okctl aws connect --deploy ``` Either way, Ownkube receives the resulting role, not your keys. You can also start the same connection from the dashboard if you'd rather not touch a terminal. ## Step 2: Pick a cluster shape Once the account is connected, choose the shape the cluster runs in. This is a real choice, not a marketing tier: it changes what you pay AWS and how the cluster is laid out. | | Starter | Production | |---|---|---| | Layout | Single AWS instance | Multi-AZ, highly available | | Ownkube platform fee | Free forever | $5 / vCPU / month + $1 / GiB RAM / month | | What you pay AWS | Directly, about $30/month for a `t3.medium` | Directly, for the EKS control plane, EC2 nodes, and storage, no markup | | Free allowance | The instance itself, no credit card required | None; every vCPU and GiB is billed from the first second | | Good for | Personal projects, prototypes, low-stakes workloads | Anything that needs to survive an availability zone going down | AWS Activate credits and other startup credit programs apply in full to whatever you owe AWS on either shape. Ownkube isn't in that billing path; it only meters its own platform fee on Production, and that fee is zero on Starter. Today this works on AWS. GCP and Azure are coming soon, not available yet, so if your app needs to land on either of those, it stays on Ownkube Compute until that ships. ## Step 3: Deploy the same app Deploy exactly the way you already do: connect the git repository, or push an image, and pick the environment variables the app needs. If the app previously ran on Ownkube Compute, the deployment settings, in most cases, carry over directly to the new cluster; you're pointing the same deploy source at your own infrastructure instead of Ownkube-managed compute. ```bash # Deploy to the cluster you just connected okctl deploy --cluster ``` Health checks and scheduled jobs move with the deployment, and on a Starter cluster you can then put a domain you own in front of it. Nothing about how you describe the app to Ownkube changes; only where it physically runs does. ## Step 4: Confirm logs, metrics, and health checks came with it Open the deployment in the dashboard the way you always would. Logs, CPU and memory metrics, and the health check status show up in the same panels, now reading from your own AWS account instead of Ownkube's shared compute. There's no separate observability stack to stand up on the new infrastructure and no second login for a monitoring tool. If something looks off after the move, the debugging path is identical to the one you already know. ## When to make the move Starter is the lower-friction path if you mainly want the app off shared compute, for a firmer performance ceiling or because a customer wants it inside their own AWS account, without paying a platform fee. Production is the right shape once the app needs to survive a zone outage or you're past the point where a single instance is enough headroom. Either way you're not committing to a rewrite to test the water: you can connect an account, try Starter, and move to Production later without touching the app itself. If you're earlier in that decision and still comparing where to run things at all, our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) and our [look at Fly.io alternatives that let you own the AWS account](/blog/fly-io-alternative-own-aws) cover the tradeoffs of managed-only platforms versus one that lets you step onto your own cloud later. And if your team already leans on an AI coding agent, [connecting it to Ownkube over MCP](/blog/connect-ai-agent-mcp) works the same way regardless of which AWS account the app ends up in. ## FAQ ### Do I need to rewrite my app to run it in my own AWS account? No. The same Dockerfile or git-push deploy, environment variables, and settings carry over. What changes is which infrastructure the platform provisions the deployment onto and who you pay for the underlying compute. ### Does Ownkube get access to my AWS account? No credentials are shared. Connecting runs a CloudFormation stack that creates a least-privilege IAM role scoped to what the platform needs; Ownkube never receives your AWS keys. ### What does Starter actually cost? Ownkube charges nothing for Starter. You pay AWS directly for the single instance it runs on, roughly $30 a month for a `t3.medium`, and that's the only bill. ### Can I use GCP or Azure instead of AWS? Not yet. AWS is the only cloud supported today. GCP and Azure are coming soon. ### What happens to my logs and metrics when I move an app to my own AWS account? They keep working. Logs, metrics, and health checks are part of the same platform layer regardless of which AWS account the cluster runs in, so they show up in the same dashboard panels after the move. ## Where Ownkube fits The point of this path is that the decision of where an app runs doesn't have to be permanent or made too early. Start on Ownkube Compute with no AWS account at all, and if the app earns its way onto your own infrastructure, move it there without a rewrite and without losing the logs, metrics, and health checks you already rely on. [Deploy your first app](https://app.ownkube.io/login). --- ## Metered or flat box: which pricing fits each app > How to choose metered vs reserved pricing per deployment on Ownkube: meter what idles, reserve what runs steadily, databases always reserved. - Canonical: https://ownkube.io/blog/metered-vs-reserved-pricing - Markdown: https://ownkube.io/blog/metered-vs-reserved-pricing.md - Published: 2026-09-02 - Author: Abhishek Jaiswal - Category: Product - Tags: pricing, metered-billing, reserved-instances, cloud-cost, ownkube-compute You do not have to pick one pricing model for your whole account. On Ownkube Compute you choose metered or a flat reserved box per deployment, so a bursty web app can meter while a steady worker sits on a fixed price, in the same project. The choice comes down to one question: does this app idle, or does it run hot around the clock? This post explains what metered and reserved billing each mean, gives the entry prices for the boxes, and lays out a decision rule simple enough to apply per app without a spreadsheet. **TL;DR** - Metered bills both CPU and memory on actual per-minute usage, so a mostly-idle app costs almost nothing on CPU. - A reserved box is a flat price whether it is busy or idle: Spark (shared CPU) from $5 a month, Core (dedicated CPU) from $58 a month. - You choose metered or reserved per deployment and can switch later. Nothing forces one model on everything. - Databases and caches are always reserved: a Postgres box from $4 a month plus storage, a Valkey cache box from $2 a month. - The rule: meter what idles, reserve what runs steadily. ## What metered pricing means Metered pricing bills you for what an app actually uses, measured per minute, on both CPU and memory. You set a CPU limit and a memory limit as ceilings, and the bill follows the meter rather than the ceiling. A limit is not a reservation, so you are not charged for headroom you set aside, only for what the app consumes up to it. The reason this matters is that most personal software is idle most of the time. A side project handling a request every few minutes has a CPU line that sits near zero between requests. On a metered deployment that idle time is nearly free, and the balance you loaded stretches across it. The metered rates are about $18 per vCPU per month and $4.50 per GiB per month, roughly a third under an equivalent [Railway bill](https://railway.com/pricing) for the same always-on shape. Memory is the part people forget. Metered on Ownkube means CPU *and* memory both bill on real usage, so an app that holds a small resident set while it idles is cheap on both lines, not just the CPU one. ## What a reserved box means A reserved box is a fixed-size instance at a flat price. It bills the same whether the app inside it is slammed or asleep. You are buying a known monthly number instead of a meter, which is exactly what you want for a workload where the meter would never rest anyway. Ownkube has two reserved compute shapes: - **Spark** is a shared-CPU box, from $5 a month. The CPU is shared with other tenants, which keeps the price low and fits small steady services and light always-on apps. - **Core** is a dedicated-CPU box, from $58 a month. You get CPU that is not shared, for workloads that need consistent performance and run hot enough to justify a dedicated instance. Because a reserved box is flat, its appeal is predictability. There is no end-of-month arithmetic and no spike to absorb. The trade is that idle time is not free: you pay the box price through the quiet hours the same as the busy ones. ## Databases and caches are always reserved Metered-versus-reserved is a choice for your app deployments. It is not a choice for data services, which are always reserved on Ownkube, because a database that scaled its price to zero when idle would also have to give up being always available. - A managed **Postgres** runs on a database box from $4 a month, plus storage at $0.25 per GB a month. It is private (in-region) by default; backups with point-in-time recovery (Personal and Team), high availability with a primary plus up to two standby instances (Team), and opt-in public access are per-database options. While it sleeps, only the storage bills. - A **Valkey** cache runs on a cache box from $2 a month. It is in-memory and ephemeral, so there is no storage charge, and it offers an opt-in public `rediss://` endpoint when you need to reach it from outside. These flat prices are a floor you can plan against. A small app with one Postgres and one Valkey has a data layer starting around $6 a month before storage, and that number does not move with traffic. ## The box entry prices at a glance One table for the reserved boxes and where the meter sits relative to them. | Box | Type | Entry price | Billing shape | |---|---|---|---| | Metered compute | Shared, usage-based | ~$18 / vCPU / mo, ~$4.50 / GiB / mo | Per-minute CPU and memory, near-zero when idle | | Spark | Reserved, shared CPU | from $5 / mo | Flat, busy or idle | | Core | Reserved, dedicated CPU | from $58 / mo | Flat, busy or idle | | Postgres | Reserved database box | from $4 / mo + $0.25 / GB / mo storage | Flat compute, storage metered | | Valkey | Reserved cache box | from $2 / mo | Flat, in-memory, no storage charge | All of it draws from the same prepaid wallet. For how that wallet caps your total spend, see [how to keep your cloud bill from surprising you](/blog/prepaid-wallet-no-surprise-bill), and for why prepaid credit that rolls over beats a postpaid meter, see [what a prepaid cloud wallet is](/blog/prepaid-wallet-vs-usage-based-billing). ## How to choose per app The decision rule is short: **meter what idles, reserve what runs steadily.** Apply it per deployment. ### Meter it when the app idles If a workload spends most of its time waiting, meter it. Web apps and APIs that handle bursts between long quiet stretches, side projects, internal tools, staging environments, and anything you spun up to try out all fit here. The idle hours are nearly free on the meter, so you pay for the bursts and little else. This is the default, and it is the right default for most personal software. ### Reserve it when it runs hot If a workload runs hot around the clock, reserve it. A busy background worker that never idles, a streaming consumer that is always draining a queue, or a service pinned near its CPU limit all day would meter continuously, and for that shape a flat box gives you a price that never moves and often costs less than the meter would. Start on Spark for a small steady service, step up to Core when it needs dedicated CPU. ### Mix them in one project Because the choice is per deployment, a single project can meter its front-end API, reserve a Spark box for a steady worker, and run a reserved Postgres and Valkey behind both. You are not choosing a billing philosophy for the account. You are matching each workload to the shape that costs less for how it actually runs, and you can switch a deployment from metered to reserved (or back) as its usage pattern settles. A quick way to sanity-check: estimate the app's average CPU usage. If average usage is a small fraction of its ceiling, meter it. If average usage sits near the ceiling most of the day, price out a reserved box, because at high steady utilization the flat rate usually wins. ## FAQ ### What is the difference between metered and reserved pricing on Ownkube? Metered bills both CPU and memory on actual per-minute usage, so an idle app is nearly free. Reserved is a flat-price box (Spark or Core) that costs the same busy or idle. You pick per deployment. ### Can I change a deployment from metered to reserved later? Yes. The choice is per deployment and you can switch as the workload's usage pattern settles. A bursty app that becomes steadily busy can move onto a reserved box, and a reserved box that mostly idles can move to the meter. ### Why can't I meter a database? A database has to stay available, so it cannot scale its price to zero the way an idle app's CPU can. Databases and caches are always reserved: Postgres from $4 a month plus storage, Valkey from $2 a month. ### Is metered always cheaper than reserved? No. Metered wins when an app idles, because you skip paying for the quiet hours. Reserved wins when an app runs hot around the clock, because a flat box beats a meter that never rests. Match the model to how the app actually runs. ### How do the box prices bill against my wallet? Every model, metered or reserved, draws down the same [prepaid wallet](/pricing) per minute. Reserved boxes draw a steady amount, metered deployments draw more when busy and almost nothing when idle, and unused wallet credit rolls over and never expires. **Reference:** For the full definition, see [Active-CPU Billing](/docs/glossary/active-cpu-billing) in the docs glossary. ## Where Ownkube fits Pricing per app is the point. Meter the workloads that idle and pay almost nothing for their quiet hours, reserve the ones that run hot and get a flat number you can plan against, and let databases and caches sit on their reserved boxes because availability is the job. It all draws one prepaid wallet, so the whole project stays inside a ceiling you set. For personal software and small teams who want each app on the pricing that suits it, that per-deployment choice is the calm default in Ownkube Compute. [Deploy your first app](https://app.ownkube.io/login). --- ## Migrate from Railway to your own AWS account > Move an app off Railway: export env vars and data, redeploy on Ownkube Compute, then connect your own AWS with no rewrite and cut over DNS. - Canonical: https://ownkube.io/blog/migrate-from-railway-to-your-own-aws - Markdown: https://ownkube.io/blog/migrate-from-railway-to-your-own-aws.md - Published: 2026-09-02 - Author: Abhishek Jaiswal - Category: How-To - Tags: railway, migration, aws, own-cloud, heroku-alternative Moving an app off Railway into an AWS account you own sounds like a rewrite, and on most platforms it is. On Ownkube it is two smaller steps that you can take on different days: redeploy the same app on Ownkube Compute first (no cloud account needed), then [connect your own AWS and move the app there with no code changes](/blog/move-app-to-your-own-aws). This guide walks the whole path, from exporting your Railway config to cutting over DNS. **TL;DR** - Decide why you are moving: a customer requirement, compliance, AWS credits, or wanting to keep the app after leaving the platform. - Export your Railway environment variables and take a fresh dump of any managed data (Postgres, volumes). - Redeploy the same container or repo on Ownkube Compute in seconds, no cloud account, and verify it on a temporary hostname. - Optionally connect your own AWS (Starter free, or Production at $5/vCPU + $1/GiB) and move the same app there with no rewrite. - Cut over DNS once the new deployment is healthy, then decommission the Railway service. ## When and why to move You do not have to move just because you can. Move when one of these is true: - **A customer's security team asks where the app runs.** "In our own AWS account" is an answer you can give on Ownkube. On Railway it is not, because the app runs on Railway's infrastructure only. - **Compliance or data-residency** requires the app in an account and region you control. - **You have AWS credits** (Activate or otherwise) you want to apply. On your own cluster there is no platform markup on AWS compute, and Activate credits apply in full. - **You want the app to outlive the platform.** Portability means you can disconnect and your apps keep running. If none of these apply and you are happy on Railway, staying is fine. If the postpaid meter or the credit that does not roll over is the actual pain, you may only need the first half of this guide (Ownkube Compute), which our [Railway alternative post](/blog/railway-alternative-2026) covers as a standalone move. ## Step 1: Export your Railway environment variables Pull the variables from the Railway service you are moving. From the [Railway CLI](https://docs.railway.com/): ```bash # Print the linked service's variables railway variables # Or dump them into a local .env you can re-import railway variables --kv > railway.env ``` Review `railway.env` and drop anything Railway-specific (internal service references, Railway-provided database URLs you will replace). Keep application secrets, third-party API keys, and feature flags. ## Step 2: Take a fresh dump of your data If your app uses a Railway-managed Postgres, take a logical dump before you touch anything. Use the connection string from the Railway dashboard or CLI. ```bash # Dump the Railway Postgres to a local file pg_dump "$RAILWAY_DATABASE_URL" --no-owner --no-privileges -Fc -f railway-db.dump ``` For volumes or uploaded files, copy them down locally or sync them to S3 now, so you have a clean snapshot that is not moving under you during the cutover. ## Step 3: Redeploy the same app on Ownkube Compute This is where the "no rewrite" starts. Ownkube Compute deploys the same container or git repo you already run, with no cloud account, no config, and no subscription to start. If you deploy from a repo, point Ownkube at it; if you ship a container, give it the image. ```bash # Deploy the current directory (git-push or Dockerfile/image both work) okctl deploy ``` Import the environment variables you exported, then provision a managed Postgres and restore the dump into it: ```bash # Set the app's env vars from the file you cleaned up okctl env set --from-file railway.env # Restore the dump into your new Ownkube-managed Postgres pg_restore --no-owner --no-privileges -d "$OWNKUBE_DATABASE_URL" railway-db.dump ``` Ownkube gives you automatic TLS and an auto-allocated hostname, so you can verify the app end to end on that temporary URL before any DNS change. The managed Postgres runs on a database box from $4/mo plus $0.25 per GB of storage, with backups available on Personal and Team; add a Valkey cache (from $2/mo) if your app used one. [Metered or a flat box is your choice per deployment](/blog/metered-vs-reserved-pricing), and unused wallet credit rolls over and never expires, so a quiet month is banked rather than burned. At this point you have a working copy of the app on Ownkube-managed compute. Many teams stop here, because it already fixes the postpaid surprise bill. If you need the app in your own AWS, continue. ## Step 4 (optional): Connect your own AWS account The same app you just verified moves into an AWS account you own with no code change. Connect the account, and Ownkube runs the workload there while its logs, metrics, and health checks come with it (the observability follows the app, so you are not flying blind after the move). Our [connect your AWS account guide](/blog/connect-your-aws-account) covers the linking step. Pricing on your own cloud: - **Starter** is free on one AWS instance. You pay AWS directly for the instance ([from about $30/mo](https://aws.amazon.com/ec2/pricing/)), with no platform fee. - **Production** is $5 per vCPU per month plus $1 per GiB of RAM per month, with no markup on AWS compute and AWS Activate credits applied in full. Because it is the same app definition, there is no rewrite: you are changing where it runs, not what it is. If Fly.io rather than Railway is your starting point, the [Fly.io alternative post](/blog/fly-io-alternative-own-aws) walks the own-AWS path from that side. ## Step 5: Cut over DNS and decommission Railway Once the new deployment (on Compute or in your own AWS) is healthy and you have watched it under real traffic on its temporary hostname: 1. On a Starter cluster, add your custom domain to the Ownkube app (subdomain and apex are both supported) and let TLS provision. 2. Lower the TTL on your DNS records a few hours ahead of the switch so propagation is quick. 3. Point the DNS record at the Ownkube endpoint. 4. Watch logs and metrics until traffic has fully shifted and the Railway service goes quiet. 5. Take a final data dump from Railway as a safety copy, then delete the Railway service so it stops billing. Keep the Railway service paused rather than deleted for a day or two if you want a fast rollback path, then remove it once you are confident. ## FAQ ### Do I have to rewrite my app to move off Railway? No. Ownkube deploys the same container or repo you already run, so redeploying on Ownkube Compute and later moving into your own AWS are configuration steps, not a rewrite. ### Can I move my Railway Postgres data? Yes. Take a logical dump with `pg_dump` from your Railway database, then restore it with `pg_restore` into a managed Postgres on Ownkube. Ownkube's Postgres is private in-region by default; backups with point-in-time recovery (Personal and Team), high availability (Team), and opt-in public access are per-database options. ### Do I need an AWS account to leave Railway? No. You can redeploy on Ownkube Compute with no cloud account at all. Connecting your own AWS is an optional later step for when a customer, compliance, or your own credits require it. ### How much does running in my own AWS cost? Starter is free on one AWS instance (you pay AWS for the instance, from about $30/mo). Production is $5 per vCPU per month plus $1 per GiB of RAM per month, with no markup on AWS compute and AWS Activate credits applied in full. ### Will I lose my logs and metrics when I move to my own AWS? No. When the app runs in your own AWS, its logs, metrics, and health checks come with it, so you keep the same observability you had on managed compute. **Reference:** For the step-by-step version, see [Migrate from Railway](/docs/migrate/from-railway) in the docs. ## Where Ownkube fits Migrating off Railway is two steps you control: redeploy the same app on Ownkube Compute to fix the billing model today, then connect your own AWS with no rewrite when you actually need it. You keep the app portable, the credit you do not spend rolls over, and the observability follows the workload wherever it runs. [Deploy your first app](https://app.ownkube.io/login). --- ## How to manage secrets and environment variables > A practical guide to setting per-deployment environment variables on Ownkube, marking values secret, and updating them without a rebuild, plus a database URL. - Canonical: https://ownkube.io/blog/manage-secrets-env-vars - Markdown: https://ownkube.io/blog/manage-secrets-env-vars.md - Published: 2026-09-01 - Author: Abhishek Jaiswal - Category: How-To - Tags: secrets, environment-variables, configuration, tutorial, ownkube-compute Every deployment on Ownkube keeps its own set of [environment variables](https://12factor.net/config), and any value you mark **Secret** is encrypted before it hits disk and masked in the dashboard the moment you save it. This guide covers adding variables, the difference between a runtime variable and a build-time argument, updating a value without triggering a rebuild, and wiring a database connection string into your app. **TL;DR** - Environment variables are set per deployment on the **Settings** tab: `KEY=value` pairs, each with a **Secret** toggle. - Plain values are stored as-is. Secret values are encrypted in Ownkube's database and masked in the UI after you save, so you can rotate them but never read them back. - Runtime variables are injected into the container when it starts. Saving triggers a rolling update, not a rebuild, so old containers drain once new ones pass health checks. - Docker build arguments are a separate, build-time input on the deployment's **Builder** tab. They apply on the next build, not the running container. - [Attach a database](/blog/add-managed-postgres-database) and Ownkube injects `DATABASE_URL` and the individual `PG*` credentials as environment variables automatically. You never type them in by hand. ## How variables are scoped Variables belong to the deployment, not the environment. `api @ Development` and `api @ Production` keep independent sets, so a value you set on one has no effect on the other. That is deliberate: it keeps a staging API key from ever leaking into production by accident. Ownkube does not yet have environment-level shared variables that every deployment in an environment inherits automatically. If you run three workers that all need the same `LOG_LEVEL`, you set it on each one today. ## Step 1: open the deployment's Settings tab Go to the deployment in your [dashboard](https://app.ownkube.io/dashboard) and scroll to **Environment variables**. Every resource type that runs a container (web, [worker](/blog/background-worker-job-queue), [job](/blog/run-scheduled-cron-jobs), function) has this section. ## Step 2: add a key and decide plain or secret Click **Add variable**. Each row has a **Key**, a **Value**, and a **Secret** toggle. ```bash NODE_ENV=production LOG_LEVEL=info STRIPE_API_KEY=sk_live_... # flip Secret on this one ``` Use plain text for anything you would not mind seeing in a screenshot: log levels, feature flags, region names. Flip **Secret** on anything else: API keys, tokens, third-party credentials, signing secrets. If you are creating a fresh deployment rather than editing an existing one, the CLI takes plain variables inline: ```bash okctl deploy create \ --name api \ --image ghcr.io/acme/api \ --tag v1.4.2 \ --port 3000 \ --env NODE_ENV=production \ --env LOG_LEVEL=info ``` Secret values are best set from the dashboard, where the Secret toggle handles encryption for you. ## Step 3: save Saving triggers a rolling update. New containers start with the updated environment, and old ones keep serving traffic until the new ones pass their health checks, then drain. There is no separate "apply" step and no downtime window to schedule. ## Step 4: confirm it landed Open the **Logs** tab and check that your process picked up the new value (a log line at boot, a `/health` response that echoes the version, whatever your app already reports). Secret values are redacted in the log stream regardless of how your app logs them, so you will not see the raw value there either way. ## Build-time arguments versus runtime variables These are two different inputs and it is worth being precise about which one you need. A **runtime environment variable** (the ones from Step 2) is injected into the container when it starts. Your process reads it with `process.env`, `os.environ`, or the equivalent. Change it and the next rolling update picks it up, no rebuild required. A **[Docker build argument](https://docs.docker.com/)** is a separate, build-time-only input on the deployment's **Builder** tab, alongside builder size and the root directory. It feeds an `ARG` in your Dockerfile and only affects the image while it is being built: ```dockerfile ARG SENTRY_RELEASE ENV SENTRY_RELEASE=$SENTRY_RELEASE ``` Set `SENTRY_RELEASE=v1.4.2` as a build argument, and it bakes into that build's image. Changing a build argument does not touch the running container. It applies on the deployment's next build, triggered by a new push or by clicking **Rebuild** on the deployment page. The rule of thumb: if a running process needs to read the value, it is a runtime variable. If only the build step needs it (a version string baked into a bundle, a flag that changes which dependencies get compiled in), it is a build argument. ## Updating a value without a rebuild This is the common case: rotating a third-party API key. Rotate it at the provider first, then paste the new value into the existing Secret entry on Ownkube and save. That save triggers a rolling update, not a rebuild, so it typically lands in under a minute and the image never changes. Nothing about your container image, your build pipeline, or your deploy history is touched, only the environment the next set of containers starts with. ## Pulling in a database connection string Attach a database deployment to a web or worker deployment, and Ownkube generates the [connection string](https://www.postgresql.org/docs/current/libpq-connect.html) and injects it as environment variables on that deployment automatically: ```bash DATABASE_URL=postgresql://user:pass@host:5432/dbname PGHOST=... PGPORT=5432 PGUSER=... PGPASSWORD=... PGDATABASE=... ``` You do not create these entries yourself and you do not paste a password anywhere. If the database's credentials rotate, the new values are picked up automatically on the next rollout, the same rolling-update path as any other variable change. ## How secrets are actually protected - **At rest**: encrypted in Ownkube's database and held in the runtime secret store, not in the deployment's plain config. - **In transit**: only ever travel over TLS. - **In the UI**: masked after save. A short prefix stays visible so you can tell which value is which without exposing the rest. - **In logs**: the structured logger redacts any variable with the Secret flag set, so a stray `console.log` of `process.env` will not print it back to you. Ownkube never shows a saved secret value in plain text again. If you lose track of what you entered, rotate it rather than trying to recover it. ## FAQ ### Can I see a secret value again after I save it? No. Once saved, a Secret entry is masked permanently in the UI. If you need to confirm a value or hand it to a teammate, rotate it and share the new value out of band. ### Do I need to redeploy manually after changing an environment variable? No. Saving a runtime variable triggers the rolling update automatically. There is nothing else to trigger. ### If I change a build argument, when does it take effect? On the deployment's next build, not immediately. Trigger one with a new push, or click **Rebuild** on the deployment page if you want it to apply without waiting for a commit. ### Can I set a variable once and have every deployment in an environment inherit it? Not yet. Environment-level shared variables are not available today; each deployment keeps its own independent set. ### Are database credentials rotated automatically? Yes. Ownkube manages database credential rotation, and the new values reach your app as environment variables on the next rollout, with no manual copy-pasting. ## Where Ownkube fits If you have spent time reconciling a `.env` file against three different dashboards, per-deployment variables with a real Secret flag are a small thing that removes a surprising amount of friction: one place to set a value, one save that rolls out safely, and a database URL you never type by hand. The same deployment, secrets and all, can later move into your own AWS account with no rewrite if you outgrow Ownkube-managed compute; see our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) for how that portability compares across platforms, or our breakdown of [self-hosted PaaS options](/blog/self-hosted-paas-comparison-2026) if you are weighing the tradeoffs more broadly. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy a Ruby on Rails app on Ownkube > Deploy a Ruby on Rails app on Ownkube with Puma, a managed Postgres database, and migrations that run automatically on release. Step by step. - Canonical: https://ownkube.io/blog/deploy-rails-app - Markdown: https://ownkube.io/blog/deploy-rails-app.md - Published: 2026-08-31 - Author: Abhishek Jaiswal - Category: How-To - Tags: rails, ruby, deploy, tutorial, ownkube-compute A [Rails](https://guides.rubyonrails.org/) app needs three things from a host: a place to run [Puma](https://puma.io/), a Postgres database it can reach, and a way to run `db:migrate` before traffic hits the new code. Ownkube covers all three from a git push, with no cloud account and no YAML to write by hand. This walks through deploying a standard Rails app on Ownkube Compute: configuring Puma, wiring up a [managed Postgres](/blog/add-managed-postgres-database), setting `RAILS_MASTER_KEY` and `SECRET_KEY_BASE`, precompiling assets, and running migrations on release. **TL;DR** - Push a Rails app with a `Dockerfile`, and Ownkube builds and deploys it behind automatic TLS. - Add an Ownkube-managed Postgres database and set `DATABASE_URL` from its connection string. - Set `RAILS_MASTER_KEY` (or `SECRET_KEY_BASE`) and `RAILS_ENV=production` as environment variables in the dashboard, not in the repo. - Precompile assets in the Docker build step, and run `db:migrate` as a release step so it happens before Puma serves the new version. - Metered billing fits Rails well: Puma sits mostly idle between requests, so the CPU line barely moves outside real traffic. ## 1. Prepare the app for production Rails is opinionated enough that most of the setup is standard Rails, not anything Ownkube-specific. Confirm the basics before you deploy: - `config/database.yml` reads its production connection from `ENV["DATABASE_URL"]`, which is the Rails default. - `config/environments/production.rb` has `config.assets.compile = false` and `config.public_file_server.enabled` set to match your asset pipeline (Propshaft or Sprockets, either works). - `Gemfile` includes `pg` for the Postgres adapter and `puma` as the app server. ```ruby # config/database.yml production: <<: *default url: <%= ENV["DATABASE_URL"] %> ``` ## 2. Write a Dockerfile Ownkube deploys from a Dockerfile. A [multi-stage build](https://docs.docker.com/build/building/multi-stage/) keeps the final image small and lets you precompile assets during the build: ```dockerfile FROM ruby:3.3-slim AS build RUN apt-get update -qq && apt-get install -y build-essential libpq-dev git curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY Gemfile Gemfile.lock ./ RUN bundle config set without 'development test' \ && bundle install --jobs 4 --retry 3 COPY . . # Precompile assets. SECRET_KEY_BASE_DUMMY lets this run without real secrets at build time. RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile FROM ruby:3.3-slim RUN apt-get update -qq && apt-get install -y libpq-dev curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY --from=build /usr/local/bundle /usr/local/bundle COPY --from=build /app /app ENV RAILS_ENV=production ENV RAILS_LOG_TO_STDOUT=true ENV RAILS_SERVE_STATIC_FILES=true EXPOSE 3000 CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"] ``` Puma's default `config/puma.rb` reads the port and worker count from environment variables already, so leave it as Rails generated it: ```ruby # config/puma.rb port ENV.fetch("PORT", 3000) workers ENV.fetch("WEB_CONCURRENCY", 2) threads_count = ENV.fetch("RAILS_MAX_THREADS", 5).to_i threads threads_count, threads_count ``` ## 3. Push the repo and create the app Connect the repository from the Ownkube dashboard (`app.ownkube.io`) or push directly with the `okctl` CLI: ```bash okctl login okctl apps create rails-app --dockerfile Dockerfile okctl deploy ``` Ownkube builds the image, allocates a hostname on `*.ownkube.app`, and issues TLS automatically, so you have a working HTTPS URL the moment it deploys. Serving the app on a domain you own is available today when it runs on a Starter (K3s) cluster in your own AWS account; custom domains on Ownkube Compute are on the roadmap, not shipped yet. ## 4. Add a managed Postgres database Create a database box from the dashboard: **New > Database > Postgres**. It provisions a private (in-region) Postgres box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team. It runs as a single private instance by default; opt-in public access and, on Team, high availability are per-database options when you need them. Ownkube gives you a `DATABASE_URL` connection string for the box. Attach it to the Rails app as an environment variable: ```bash okctl env set DATABASE_URL="postgres://user:password@db-host:5432/rails_production" --app rails-app ``` Because `config/database.yml` already reads from `ENV["DATABASE_URL"]`, no further Rails configuration is needed. ## 5. Set your secrets Rails needs its master key or secret key base to decrypt credentials and sign sessions. Set it as a [secret environment variable](/blog/manage-secrets-env-vars) in the dashboard rather than committing `config/master.key`: ```bash okctl env set RAILS_MASTER_KEY="$(cat config/master.key)" --app rails-app okctl env set RAILS_ENV=production --app rails-app ``` If you use `SECRET_KEY_BASE` directly instead of the encrypted credentials file, set that instead: ```bash okctl env set SECRET_KEY_BASE="$(bundle exec rails secret)" --app rails-app ``` Never commit `config/master.key` to the repository. Environment variables set through Ownkube are encrypted at rest and only injected into the running container. ## 6. Run migrations on release Migrations need to run before the new Puma workers start serving requests, not after. Configure `db:migrate` as a release command so Ownkube runs it once per deploy, ahead of the rollout: ```bash okctl apps update rails-app --release-command "bundle exec rails db:migrate" ``` With a release command set, every `okctl deploy` runs the migration against the connected Postgres box first, and only rolls Puma workers over to the new image once it succeeds. If the migration fails, the previous version keeps serving, which is the zero-downtime rollout behavior working in your favor. ## 7. Scale and watch it run A fresh Rails app usually starts fine on a small metered ceiling: ```bash okctl deploy --cpu-limit 500m --memory-limit 1Gi ``` Turn on [horizontal autoscaling](/blog/autoscale-cpu-memory) if traffic is uneven, so Ownkube adds Puma instances under load and scales back down when it is quiet: ```bash okctl apps update rails-app --autoscale-min 1 --autoscale-max 4 --autoscale-cpu-target 70 ``` Logs and metrics for the app and the database box are both in the dashboard, so a slow migration or a Puma worker that keeps restarting shows up in one place. ## Moving to your own AWS later If the app outgrows the shared Compute tier, or a customer contract requires it to run in a named AWS account, the same Dockerfile and Rails app move to your own AWS with no rewrite. Ownkube provisions the instance, wires up the same deploy flow, and brings the logs, metrics, and health checks with it. That portability is one of the things that separates Ownkube from Railway, which only runs on Railway's infrastructure. Our [Railway alternative comparison](/blog/railway-alternative-2026) covers the tradeoffs in more depth. ## FAQ ### Does Ownkube support Rails credentials and Active Storage? Yes. Encrypted credentials work through `RAILS_MASTER_KEY` as shown above. Active Storage works against any S3-compatible bucket you configure; Ownkube does not provide managed object storage directly, so point it at your own bucket. ### Do I need a Dockerfile, or can Ownkube detect Rails automatically? A Dockerfile gives you full control over the Ruby version, native gems, and the asset precompile step, and is the most reliable path for Rails. Simpler apps without native dependencies can also deploy straight from source. ### Can I run Sidekiq or another background worker alongside the web app? Yes. Deploy the worker as a second Ownkube app pointed at the same Postgres connection string (and a Valkey cache box from $2 a month if you need Redis for the queue), so the web app and worker scale independently. ### What happens if my wallet balance runs out mid-month? The app pauses and its data, including the Postgres box, is kept. Adding credit or the next monthly plan load resumes it. Unused wallet credit rolls over and never expires, so a quiet month is not credit lost. ### Can I roll back a bad deploy? Yes. Ownkube keeps prior image versions and supports zero-downtime rollback from the dashboard or `okctl`, so a migration or code issue does not require a manual recovery. **Reference:** For the always-current version of this walkthrough, see the [Deploy Ruby on Rails](/docs/deploy/rails) guide in the docs. ## Where Ownkube fits Rails wants a real database, a release step for migrations, and an app server that survives restarts, and Ownkube gives you all three without provisioning a cloud account first. Push the Dockerfile, attach a managed Postgres, set your secrets, and the release command handles the rest. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## Does Railway credit roll over or expire? > How Railway's plan credit and postpaid billing work, and how a prepaid wallet with rollover credit that never expires differs. - Canonical: https://ownkube.io/blog/does-railway-credit-expire - Markdown: https://ownkube.io/blog/does-railway-credit-expire.md - Published: 2026-08-31 - Author: Abhishek Jaiswal - Category: Engineering - Tags: railway, prepaid-wallet, cloud-cost, pricing, railway-alternative Short answer: Railway's plan credit is a monthly usage allowance scoped to the billing cycle, so it does not roll over, and Railway itself is postpaid, so there is no stored balance to carry forward in the first place. That is worth understanding precisely, because it is the difference between "credit" as a monthly allowance and "credit" as money you loaded and keep. This post explains Railway's model fairly, then contrasts it with a prepaid wallet where unused credit rolls over and never expires. **TL;DR** - Railway is postpaid: [usage meters by the second](https://docs.railway.com/) and you pay after the fact. The plan includes a monthly usage allowance, not a stored balance. - That allowance is scoped to the cycle, so it does not roll over. A quiet month does not bank credit you can spend next month. - A prepaid wallet is the opposite order: you load a dollar balance up front, usage draws it down, and whatever you do not spend stays on the balance. - On Ownkube, unused wallet credit rolls over and never expires. Only the separate monthly build-minutes allowance resets. - Prepaid also caps the downside: at zero balance apps pause and data is kept, so the most you can spend is the balance you loaded. ## How Railway credit and billing actually work Railway bills postpaid on metered usage. Your services meter CPU, memory, and other resources by the second, and the total is charged after the fact against your payment method. The plan you are on ([Hobby, Pro](https://railway.com/pricing)) comes with an amount of included usage each month, which people usually call "credit." The important detail is what that credit is. It is a monthly usage allowance attached to the billing cycle, not a wallet balance you topped up. It offsets that month's metered usage. Because it is scoped to the cycle, a month where you used less than the allowance does not bank the remainder for a future month. And because the underlying model is postpaid, there is no stored dollar balance sitting in your account to roll forward in the first place: the meter runs, the allowance offsets it, and anything above the allowance is billed to your card. So "does Railway credit expire" is really two questions: - Does the monthly plan allowance roll over? No. It is scoped to the cycle. - Is there a stored prepaid balance that could expire? No, because Railway is postpaid; there is no loaded balance to expire. This is a normal, common model, and it is efficient when you are actively watching usage. It is only a mismatch if what you wanted was to pay in and keep what you did not use. ## What "prepaid, rolls over, never expires" changes A prepaid wallet reverses the order. Instead of usage running first and a bill arriving second, you load a dollar balance, and everything you run draws that balance down per minute for what it actually uses. Two consequences follow. **Unused credit carries forward.** Whatever you did not spend this month stays on your balance, and next month's load adds on top of it. A quiet month is banked, not burned. Nothing about the balance is tied to the cycle boundary, so it does not expire. **The downside is bounded.** When the balance reaches zero, your apps pause and your data stays put until you add credit or the next monthly load arrives. The most you can spend is the balance you loaded, because there is no card behind it running ahead of you. That is the structural fix for the surprise bill, which we cover in full in [how to keep your cloud bill from surprising you](/blog/prepaid-wallet-no-surprise-bill). On Ownkube the wallet is the billing model for Ownkube Compute. Your plan loads credit each month (Personal loads $5, Team loads $20), usage draws it down, and unused credit rolls over and never expires. One honest caveat: the separate build-minutes allowance (300 minutes on Personal, 1,500 on Team) does reset monthly and does not roll over. It is the wallet dollar balance that carries forward, not the build allowance. ## Railway vs a prepaid wallet, side by side | | Railway | Ownkube prepaid wallet | |---|---|---| | Billing order | Postpaid: usage first, bill after | Prepaid: load balance, usage draws it down | | What "credit" is | Monthly usage allowance, cycle-scoped | Loaded dollar balance you own | | Rolls over? | No, allowance is per cycle | Yes, unused credit rolls over | | Expires? | No stored balance to expire | Never expires | | Downside cap | Card is charged for overage | At zero, apps pause and data is kept | | Monthly reset | Usage allowance | Build minutes only (not the wallet dollars) | The row that matters most is the first one. Everything else follows from postpaid vs prepaid. ## Which model should you want? If you actively watch usage and your spend is steady, Railway's postpaid model is fine and the lack of rollover never bites you. If you would rather bound the worst case up front, or you have quiet months you want to bank, a prepaid wallet fits better. It is less about the rate and more about who is holding the risk of a bad month. For a wider look at how billing models behave at the end of the month, see our [Railway, Render, and Northflank comparison](/blog/railway-vs-render-vs-northflank-2026), and for the full alternative case, [when the metered bill pushes you off Railway](/blog/railway-alternative-2026). ## FAQ ### Does Railway credit roll over to the next month? No. Railway's plan credit is a monthly usage allowance scoped to the billing cycle, so an unused remainder does not carry into the next month. ### Does Railway credit expire? There is no stored prepaid balance on Railway to expire, because Railway is postpaid: usage meters and is billed after the fact, with the monthly plan allowance offsetting that cycle's usage. A prepaid wallet like Ownkube's does hold a balance, and that balance never expires. ### What is the difference between prepaid and postpaid cloud billing? Postpaid meters usage and bills you after, against a card. Prepaid has you load a balance first, and usage draws it down until you top up. Prepaid caps the worst case at the balance you loaded; postpaid can bill past what you expected before you notice. ### Does unused Ownkube credit expire? No. Unused wallet credit rolls over to the next month and never expires. The only thing that resets monthly is the separate build-minutes allowance, not the wallet dollar balance. ### What happens when a prepaid balance hits zero? On Ownkube, your apps pause and your data is kept. Nothing is deleted. A top-up (from a $5 minimum) or the next monthly plan load resumes them. ## Where Ownkube fits If you want credit that behaves like money you own, load a prepaid wallet, keep what you do not spend, and bound the worst case at the balance instead of a card. That is how Ownkube Compute bills: Personal $5/mo, Team $20/mo, unused credit rolls over and never expires, and you choose metered or a flat box per app. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy a Next.js app on Ownkube > Deploy a Next.js app on Ownkube using the standalone build output, environment variables, an optional managed Postgres, and automatic TLS on your hostname. - Canonical: https://ownkube.io/blog/deploy-nextjs-app - Markdown: https://ownkube.io/blog/deploy-nextjs-app.md - Published: 2026-08-30 - Author: Abhishek Jaiswal - Category: How-To - Tags: nextjs, javascript, deploy, tutorial, ownkube-compute A Next.js app in a container needs one setting most tutorials skip: `output: "standalone"`, which trims the build down to only the files `next start` actually needs instead of the full `node_modules` tree. Get that right and the rest of a Next.js deploy on Ownkube is a normal git push. This walks through deploying a Next.js app (App Router or Pages Router, both work) on Ownkube Compute: the standalone build, environment variables split between build time and runtime, an optional managed Postgres for apps using an ORM, and automatic TLS. **TL;DR** - Set `output: "standalone"` in `next.config.js` so the Docker image ships a minimal server instead of the whole `node_modules` folder. - Push the app with a `Dockerfile`, and Ownkube builds it, deploys it, and issues TLS automatically on your `*.ownkube.app` hostname. - `NEXT_PUBLIC_*` variables get baked into the build; everything else is a runtime environment variable set in the dashboard. - Add an Ownkube-managed Postgres database if you use Prisma or another ORM, and point the connection string at it. - Turn on horizontal autoscaling once traffic is uneven, since Next.js's SSR path is CPU-bound per request in a way static pages are not. ## 1. Enable standalone output In `next.config.js`, add the [standalone output mode](https://nextjs.org/docs/app/api-reference/config/next-config-js/output): ```js // next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone", }; module.exports = nextConfig; ``` This changes what `next build` produces: instead of a build that expects the full project and `node_modules` present at runtime, it emits a self-contained `.next/standalone` folder with only the files the server needs, plus a minimal `server.js` entry point. That is what keeps the final Docker image small. ## 2. Write a Dockerfile A [multi-stage build](https://docs.docker.com/build/building/multi-stage/) keeps dependencies out of the final image: ```dockerfile FROM node:20-slim AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci FROM node:20-slim AS build WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # NEXT_PUBLIC_* values must be present at build time; they end up in the client bundle ARG NEXT_PUBLIC_API_URL ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL RUN npm run build FROM node:20-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.next/standalone ./ COPY --from=build /app/.next/static ./.next/static COPY --from=build /app/public ./public EXPOSE 3000 CMD ["node", "server.js"] ``` The build stage takes `NEXT_PUBLIC_API_URL` as a build argument because Next.js inlines `NEXT_PUBLIC_*` variables into the client JavaScript at build time. A value set only at runtime in the dashboard will not reach client-side code; it has to be present when `npm run build` runs. ## 3. Push the repo and create the app Connect the repository from the Ownkube dashboard (`app.ownkube.io`) or push directly with the `okctl` CLI: ```bash okctl login okctl apps create nextjs-app --dockerfile Dockerfile okctl deploy ``` Ownkube builds the image, allocates a hostname on `*.ownkube.app`, and issues TLS automatically. No reverse proxy or certificate to set up by hand. ## 4. Set your environment variables Split variables by when Next.js actually needs them: ```bash # Server-only, read at runtime by API routes or Server Components okctl env set DATABASE_URL="postgres://user:password@db-host:5432/nextjs_production" --app nextjs-app okctl env set AUTH_SECRET="$(openssl rand -base64 32)" --app nextjs-app # Client-visible, must also be passed as a build argument (see step 2) okctl env set NEXT_PUBLIC_API_URL="https://nextjs-app.ownkube.app/api" --app nextjs-app ``` Environment variables set through Ownkube are encrypted at rest and only injected into the running container. Anything without the `NEXT_PUBLIC_` prefix stays server-side and never ships to the browser. ## 5. Add a managed Postgres database, if you use an ORM A Next.js app using [Prisma](https://www.prisma.io/docs/orm/prisma-migrate), Drizzle, or another ORM for a database-backed API route needs a real Postgres instance. Create a database box from the dashboard: **New > Database > Postgres**, walked through in full in [add a managed Postgres database to your app](/blog/add-managed-postgres-database). It provisions a private (in-region) Postgres box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team. It runs as a single instance by default; you can turn on opt-in public access and, on Team, high availability (a primary plus up to two standby instances) per database when you need them. With Prisma, point `DATABASE_URL` at the connection string and run migrations as part of the build: ```dockerfile # In the build stage, after copying the schema RUN npx prisma generate RUN npx prisma migrate deploy ``` `prisma migrate deploy` applies pending migrations without prompting, which is what you want in a non-interactive build. A purely static or content-fetching Next.js app can skip a database entirely. ## 6. Scale and watch it run A fresh Next.js app usually starts fine on a small metered ceiling: ```bash okctl deploy --cpu-limit 500m --memory-limit 512Mi ``` Turn on [horizontal autoscaling](/blog/autoscale-cpu-memory) if traffic is uneven, so Ownkube adds server instances under load and scales back down when it is quiet: ```bash okctl apps update nextjs-app --autoscale-min 1 --autoscale-max 4 --autoscale-cpu-target 70 ``` Server-side rendering and API routes are CPU work per request in a way a purely static export is not, so an SSR-heavy app benefits more from autoscaling headroom than a mostly-static one. Metered billing charges CPU and memory on actual per-minute usage, so a quiet Next.js app between visits barely moves the wallet. Logs and metrics for the app live in the dashboard, so a slow render or a route that keeps erroring shows up in one place. ## Moving to your own AWS later If the app needs to run inside your own AWS account later, for compliance reasons or because a client wants their name on the infrastructure, the same Dockerfile and Next.js app move over with no rewrite. Ownkube provisions the instance, wires up the same deploy flow, and brings the logs, metrics, and health checks with it. That is not something a platform like Railway offers, since it only runs your app on its own infrastructure. Our [Railway alternative comparison](/blog/railway-alternative-2026) walks through where that gap tends to matter. ## FAQ ### Do I need `output: "standalone"`, or does the default build work? The default build works, but it expects the full project and `node_modules` present at runtime, which makes for a much larger Docker image and a slower deploy. Standalone output is the setting almost every Next.js Docker guide converges on, and it is the one used in the Dockerfile above. ### Does Ownkube support the App Router and Server Actions? Yes, both the App Router and the older Pages Router run the same way, since `next start` in the standalone build serves whatever the app builds. Server Actions run as part of the same Node process and read server-side environment variables normally. ### How do I handle a value that is different between preview and production? Set the environment variable per app in the dashboard or with `okctl env set --app `, and use a separate Ownkube app for each environment you want isolated, each with its own hostname, environment variables, and (if needed) database. ### What happens if my wallet balance runs out mid-month? The app pauses and any attached database keeps its data. Adding credit or the next monthly plan load resumes it. Unused wallet credit rolls over and never expires. ### Can I use a custom domain? An automatic `*.ownkube.app` hostname with TLS is provisioned for every app the moment it deploys, so you always have a working URL with no configuration. Custom domain support on Ownkube Compute is on the roadmap; it is available today on Ownkube's Starter (BYOC) clusters. **Reference:** For the always-current version of this walkthrough, see the [Deploy Next.js](/docs/deploy/nextjs) guide in the docs. ## Where Ownkube fits Next.js wants a slim server image, a clean split between build-time and runtime configuration, and a database only when the app actually uses one, and Ownkube gives you all three without provisioning a cloud account first. Set standalone output, push the Dockerfile, set your environment variables, and the hostname and TLS are already waiting. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to self-host n8n on Ownkube > Deploy the official n8nio/n8n image on Ownkube with a managed Postgres database, working webhooks, and automatic TLS on a stable public address. - Canonical: https://ownkube.io/blog/deploy-n8n - Markdown: https://ownkube.io/blog/deploy-n8n.md - Published: 2026-08-29 - Author: Abhishek Jaiswal - Category: How-To - Tags: n8n, self-hosting, automation, deploy, tutorial n8n is a workflow automation tool worth self-hosting the moment you have more than a couple of workflows, since the hosted cloud plans price by execution and self-hosting removes that ceiling. This walks through deploying the official `n8nio/n8n` image on Ownkube with a Postgres database behind it, correct webhook URLs, and TLS on a stable public address. **TL;DR** - n8n defaults to a bundled SQLite database, which does not hold up well under concurrent workflow executions. Point it at [Postgres](https://www.postgresql.org/docs/) with the `DB_TYPE=postgresdb` env vars instead. - Deploy the official `n8nio/n8n` image, attach a [managed Postgres database](/blog/add-managed-postgres-database) box, and set `N8N_HOST`, `N8N_PROTOCOL`, and `WEBHOOK_URL` to the app's public address before you build any workflow that uses a webhook trigger. - Set `N8N_ENCRYPTION_KEY` yourself so saved credentials survive restarts and redeploys. - Every Ownkube app gets an `ownkube.app` address with automatic TLS, which matters for n8n since most webhook-based integrations (Stripe, GitHub, Slack) refuse plain HTTP callback URLs. - A database box starts at $4/month plus storage, and the app itself can sit on a small reserved box or run metered depending on how often workflows fire. ## What you need before you start - The image reference `n8nio/n8n:latest`, or a pinned version tag if you want reproducible upgrades. - A subdomain for the app on `ownkube.app`, for example `automate-yourteam.ownkube.app`. Webhook-triggered workflows need a stable URL, so pick it before you build any. - A generated encryption key, set explicitly rather than letting n8n generate one on first boot, since a regenerated key invalidates saved credentials. ## 1. Create a managed Postgres database n8n supports Postgres as its production-grade database option, and it is the one to use for anything beyond a single-user test instance. Create a database box on Ownkube (from $4/month plus storage at $0.25/GB/month) and note the host, port, database name, user, and password. ## 2. Deploy the n8n image Create the app from `n8nio/n8n:latest`, or start from this Dockerfile if you want to pin a version: ```dockerfile FROM n8nio/n8n:1.60.1 ``` Point the app at port `5678`, which is what the image listens on by default. ## 3. Set the environment variables ```bash # Database DB_TYPE=postgresdb DB_POSTGRESDB_HOST= DB_POSTGRESDB_PORT=5432 DB_POSTGRESDB_DATABASE=n8n DB_POSTGRESDB_USER= DB_POSTGRESDB_PASSWORD= # Host and webhooks N8N_HOST=automate-yourteam.ownkube.app N8N_PROTOCOL=https N8N_PORT=5678 WEBHOOK_URL=https://automate-yourteam.ownkube.app/ # Security N8N_ENCRYPTION_KEY= GENERIC_TIMEZONE=UTC ``` Set `N8N_HOST`, `N8N_PROTOCOL`, and `WEBHOOK_URL` to the app's public address before you build anything that depends on an inbound webhook. n8n bakes the [webhook base URL](https://docs.n8n.io/) into every workflow's trigger node, so changing it later means editing each affected workflow rather than a single config change. ## 4. Keep the app stateless With `DB_TYPE=postgresdb`, workflows, executions, and credentials live in Postgres, not on the container's disk. The one thing n8n would otherwise keep in `/home/node/.n8n` is the encryption key it generates on first boot, and a redeploy would lose it. Setting `N8N_ENCRYPTION_KEY` explicitly (step 3) removes that dependency, so the app can restart or redeploy without invalidating any saved credential. ## 5. Verify webhooks The app's `ownkube.app` address serves HTTPS from the first deploy, with no DNS to set up. Confirm the app is reachable and that a test webhook round-trips correctly: ```bash curl -I https://automate-yourteam.ownkube.app # From n8n's UI, use the "Listen for test event" button on a webhook # trigger node, then send a test request to confirm the URL is public # and TLS-terminated correctly. curl -X POST https://automate-yourteam.ownkube.app/webhook-test/ ``` Most third-party services that trigger n8n workflows (Stripe, GitHub, Slack, Typeform) require HTTPS callback URLs and reject plain HTTP, so this step matters more for n8n than for a typical web app. ## Sizing the deployment A handful of scheduled workflows that run occasionally is a light workload and fits comfortably on [metered pricing](/blog/prepaid-wallet-no-surprise-bill), billed per minute. A busier setup with many active webhook triggers and frequent executions runs closer to constant load, which is a better fit for a flat Spark or Core box so the price does not move with execution volume. You choose per deployment and can switch later without redeploying the underlying image. If this instance eventually needs to move off shared infrastructure, for example because a workflow now touches data with residency requirements, the same `n8nio/n8n` image and Dockerfile run unchanged in your own AWS account under Ownkube's own-cloud option, with the same logs and health checks attached. No rewrite, just a different compute target. For a broader look at self-hosted automation platforms next to managed alternatives, see our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026). ## FAQ ### Does n8n need Postgres, or does SQLite work? n8n ships with SQLite by default and it works for light single-user use, but the project [recommends Postgres](https://docs.n8n.io/hosting/configuration/environment-variables/database/) for anything with concurrent executions or production reliability expectations. Set `DB_TYPE=postgresdb` and the matching `DB_POSTGRESDB_*` variables to use it. ### Why do my webhook URLs return the wrong host? `WEBHOOK_URL` (and `N8N_HOST`/`N8N_PROTOCOL`) must match the app's public address before you create webhook-triggered workflows. If you change the address later, existing webhook trigger nodes keep the old URL baked in and need to be recreated or manually updated. ### What happens if I lose the encryption key? `N8N_ENCRYPTION_KEY` decrypts stored credentials. If it changes or is lost, every saved credential in existing workflows stops working and needs to be re-entered. Set it explicitly as an environment variable rather than letting n8n generate one you have not saved. ### Can I run n8n without a persistent volume? Yes, as long as it uses Postgres and `N8N_ENCRYPTION_KEY` is set explicitly. Without the key, a redeploy resets `/home/node/.n8n` and n8n generates a new one, which breaks every saved credential. ### What does this cost to run? A Postgres database box starts at $4/month plus storage. The n8n app itself can run metered for light, occasional workflow use or on a Spark box (from $5/month) for steadier automation. ## Where Ownkube fits n8n's official image needs a real database and a stable public URL to be reliable, and Ownkube Compute covers both without a cloud account to configure first. Deploy the image, attach managed Postgres, set the encryption key, and your webhooks get an HTTPS address from the first deploy. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy a Go app on Ownkube > Deploy a Go app on Ownkube with a multi-stage Dockerfile for a small static binary, environment-based config, an optional managed Postgres, and metered billing. - Canonical: https://ownkube.io/blog/deploy-go-app - Markdown: https://ownkube.io/blog/deploy-go-app.md - Published: 2026-08-28 - Author: Abhishek Jaiswal - Category: How-To - Tags: go, golang, deploy, tutorial, ownkube-compute A [Go](https://go.dev/doc/) binary is close to the ideal thing to deploy: no runtime to install, a small static executable, and a memory footprint that barely moves at idle. Ownkube runs it as a Dockerfile-based deploy, with automatic TLS and an optional managed Postgres if the app needs a database. This walks through deploying a Go app on Ownkube Compute: a multi-stage Dockerfile that produces a small static binary, configuration through [environment variables](/blog/manage-secrets-env-vars), an optional [Postgres connection](/blog/add-managed-postgres-database), and why Go's low idle footprint fits Ownkube's metered billing especially well. **TL;DR** - A [multi-stage Dockerfile](https://docs.docker.com/build/building/multi-stage/) compiles a static Go binary in one stage and copies just the binary into a minimal final image, often under 20 MB. - Configure the app entirely through environment variables read at startup, including `PORT` and `DATABASE_URL`. - Add a managed Postgres if the app needs one, and connect with `database/sql` and a driver like `pgx` or `lib/pq`. - Metered billing rewards Go's footprint directly: a Go service idles at a few megabytes of RAM and near-zero CPU, so the meter barely moves between requests. - Autoscaling and zero-downtime rollouts work the same way as any other Ownkube app. ## 1. Read configuration from the environment Idiomatic Go config is a handful of `os.Getenv` calls at startup, with `PORT` read the same way Ownkube injects it for any other language: ```go // main.go package main import ( "log" "net/http" "os" ) func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } mux := http.NewServeMux() mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"ok"}`)) }) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello from Ownkube")) }) log.Printf("listening on %s", port) if err := http.ListenAndServe(":"+port, mux); err != nil { log.Fatal(err) } } ``` ## 2. Write a multi-stage Dockerfile The build stage compiles a static binary with `CGO_ENABLED=0`, and the final stage copies only that binary into a minimal base, so the shipped image is small and starts fast: ```dockerfile FROM golang:1.23 AS build WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server . FROM gcr.io/distroless/static-debian12 COPY --from=build /app/server /server EXPOSE 8080 ENTRYPOINT ["/server"] ``` `distroless/static-debian12` has no shell and no package manager, which keeps the attack surface and the image size down. If you need a shell for debugging, swap it for `alpine:3.20` and add `RUN apk add --no-cache ca-certificates` before the final `COPY`. ## 3. Push and create the app ```bash okctl login okctl apps create go-app --dockerfile Dockerfile okctl deploy ``` Ownkube builds the image, allocates a `*.ownkube.app` hostname, and issues TLS automatically, so you have a working HTTPS URL the moment it deploys. ## 4. Set environment variables ```bash okctl env set LOG_LEVEL=info --app go-app okctl env set API_TOKEN="your-secret-token" --app go-app ``` Environment variables set this way are encrypted at rest and injected into the container at start, so secrets never need to live in the image or the repo. ## 5. Add a managed Postgres, if the app needs one Create a Postgres box from **New > Database > Postgres** in the dashboard. It is a private (in-region) box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team. It runs as a single instance by default; opt-in public access and, on Team, high availability (a primary plus up to two standby instances) are per-database options. ```bash okctl env set DATABASE_URL="postgres://user:password@db-host:5432/go_app" --app go-app ``` Connect with the standard library and a driver such as `pgx`: ```go import ( "database/sql" "os" _ "github.com/jackc/pgx/v5/stdlib" ) db, err := sql.Open("pgx", os.Getenv("DATABASE_URL")) if err != nil { log.Fatal(err) } defer db.Close() ``` ## 6. Wire up the health check and autoscaling Point Ownkube's health check at the `/health` route from step 1, so a rollout only completes once the new instance is actually serving: ```bash okctl apps update go-app --health-check-path /health ``` Turn on [horizontal autoscaling](/blog/autoscale-cpu-memory) for uneven traffic: ```bash okctl apps update go-app --autoscale-min 1 --autoscale-max 5 --autoscale-cpu-target 70 ``` ## 7. Why Go's footprint fits metered billing Ownkube's metered pricing bills CPU and memory on actual per-minute usage, roughly $18 per vCPU per month and $4.50 per GiB per month. A Go binary is the shape that pricing model rewards most directly: no VM or interpreter warming up, a resident set often in the tens of megabytes, and CPU that drops to near zero the instant a request finishes. Set a small ceiling and let the meter do the rest: ```bash okctl deploy --cpu-limit 250m --memory-limit 256Mi ``` For a low-traffic API or a webhook receiver, that low idle footprint means the wallet balance stretches a long way between top-ups, since a limit is a ceiling, not a reservation. You are billed for what the process actually uses up to that ceiling, not for the ceiling itself. ## Moving to your own AWS later If the app later needs to run in a named AWS account, your own or a customer's, the same Dockerfile moves with no rewrite. Ownkube provisions the instance and keeps the same deploy flow, logs, metrics, and health checks attached to it. That is a path Railway does not offer, since it only runs on Railway's own infrastructure. Our [Railway alternative comparison](/blog/railway-alternative-2026) covers what changes when you make that move. ## FAQ ### Does Ownkube support Go modules and private dependencies? Yes. `go mod download` runs during the [build stage](https://docs.docker.com/language/golang/) as normal. For a private module in a private repo, add a `GOPRIVATE` build argument and the appropriate git credentials as build-time secrets. ### Do I need CGO for database drivers? No. Pure-Go drivers like `pgx` (Postgres) work with `CGO_ENABLED=0`, which keeps the static binary fully portable across the distroless base image. Drivers that wrap a C library, like some SQLite bindings, need CGO enabled and a base image with the right shared libraries. ### How small does the final image get? A typical Go HTTP service with a distroless base lands in the 10 to 20 MB range, mostly the binary itself plus CA certificates, which keeps build and deploy times short. ### Can I run a background worker in the same repo? Yes. Build a second binary from the same module (a second `go build` target or a build arg that selects `cmd/worker` versus `cmd/server`) and deploy it as a separate Ownkube app pointed at the same `DATABASE_URL`. ### Is there a free tier for a small Go service? No, Compute has no free tier, but you don't need a subscription either: top up from $5 and pay per minute. The Personal plan is $5 a month and loads $5 of wallet credit you then spend. Either way, unused credit rolls over and never expires, so a low-traffic Go service that barely touches the meter carries balance forward month to month. **Reference:** For the always-current version of this walkthrough, see the [Deploy Go](/docs/deploy/go) guide in the docs. ## Where Ownkube fits Go's small static binary and near-zero idle footprint line up well with metered billing that charges for actual CPU and memory rather than instance size. Ship a multi-stage Dockerfile, read config from the environment, attach a managed Postgres if you need one, and the health check and autoscaling handle the rest. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to self-host Metabase on Ownkube > A step-by-step guide to self-hosting Metabase on Ownkube with the official image, a managed Postgres database instead of the default H2 file, and automatic TLS. - Canonical: https://ownkube.io/blog/deploy-metabase - Markdown: https://ownkube.io/blog/deploy-metabase.md - Published: 2026-08-28 - Author: Abhishek Jaiswal - Category: How-To - Tags: metabase, self-hosting, analytics, deploy, tutorial Metabase ships with an embedded H2 database by default, which is fine for a five-minute demo and a bad idea the moment more than one person relies on the dashboards. Self-hosting it properly means putting its application data on a real Postgres database from the start, and Ownkube's Metabase recipe does that wiring for you with the official image. This walks through deploying it, the exact environment variables Metabase reads to find that database, why memory sizing matters more than it looks like it should, and the manual path if you want your own image instead. **TL;DR** - Deploy Metabase from Ownkube's Marketplace using the official `metabase/metabase` image, no server to patch yourself. - Ownkube wires a [managed Postgres database](/blog/add-managed-postgres-database) into Metabase's `MB_DB_*` environment variables, so your dashboards and questions live outside the container instead of in the default embedded H2 file. - The JVM needs real headroom: Ownkube's recipe requests 1 GiB and caps memory at 2 GiB, size your box with that in mind. - Metabase's own setup wizard creates the admin account on first boot. Ownkube does not bootstrap it for you the way it does for some other recipes. - The app gets a public hostname with [automatic TLS](/blog/automatic-https-custom-domain) once its health check passes. ## What you need before you start Just an Ownkube account, no cloud account of your own. The Personal plan is $5 a month, loaded straight into a wallet you then spend on the database box and the app's compute. Sign in at [app.ownkube.io](https://app.ownkube.io). ## Step 1: Open the Metabase recipe in the Marketplace Ownkube's Marketplace has a Metabase recipe at `app.ownkube.io/dashboard/marketplace/metabase` that provisions the Metabase web app and a managed Postgres database together. Sign in to browse and deploy. Marketplace is also one click away in the dashboard's top nav and the ⌘K palette. ## Step 2: Choose your Postgres box size, and use it This is the step that matters most for self-hosting Metabase correctly. Left alone, Metabase stores everything (your dashboards, saved questions, users, and permissions) in a single embedded H2 file inside the container. That file does not survive a redeploy cleanly and was never meant to carry more than a quick trial. The recipe instead provisions a managed Postgres database box, starting at $4 a month plus storage at $0.25 per GB, and points Metabase at it before it ever boots. ## Step 3: Let Ownkube wire the database connection Metabase does not take a single connection URL for its application database. It reads the connection as separate fields, and Ownkube sets each one from the managed Postgres database it just provisioned: ```bash MB_DB_TYPE=postgres MB_DB_HOST= MB_DB_PORT=5432 MB_DB_DBNAME= MB_DB_USER= MB_DB_PASS= ``` Setting `MB_DB_TYPE=postgres` is what actually turns off the default H2 file. Miss that one variable and Metabase quietly falls back to embedded storage even if the rest of the fields are correct, which is the most common mistake in a manual Metabase deploy. The connection runs without TLS by default between the app and the database, which is fine since both sit privately in the same project. ## Step 4: Size memory for the JVM Metabase runs on the JVM, and the JVM wants headroom before it wants CPU. Ownkube's recipe requests 250m CPU and 1 GiB of memory, with a limit of 1 full CPU and 2 GiB of memory. If you are sizing a box by hand for a custom deploy, treat 1 GiB as the floor, not a comfortable ceiling. Undersizing memory here is the fastest way to see Metabase crash-loop during startup rather than run slowly. The health check also gives it room: Ownkube polls `/api/health` for up to five minutes after boot before deciding the deploy failed, because a first boot has to run Metabase's own database migrations against your Postgres box, and that takes real time. ## Step 5: Deploy and finish setup in Metabase's own wizard Once the deployment is healthy, Ownkube assigns it a public hostname on the shared region edge with TLS already in place. Open it, and Metabase runs its own first-run setup wizard: you create the admin account and can add your first data source right there. Ownkube does not pre-generate this account the way it does for some other Marketplace recipes; it is entirely Metabase's own onboarding. ## Deploying a custom Metabase image instead If you need a driver or plugin that is not in the stock image, connect a GitHub repository with a Dockerfile built `FROM metabase/metabase` instead of using the recipe. Ownkube builds and deploys it the same way it would any other container. You would then add a managed [Postgres database](https://www.postgresql.org/docs/) to the project and set the same `MB_DB_*` variables yourself, copying the host, port, database name, user, and password from the database's Connect tab. The Marketplace recipe exists specifically so you do not have to do that wiring by hand unless you actually need a customized image. ## Move it to your own AWS later, with no rewrite If Metabase's query load or your team outgrows what you want to run on Ownkube Compute, the same app, the same official image, and the same Postgres data move into [your own AWS account](/blog/move-app-to-your-own-aws) with no rewrite. Its logs, metrics, and health checks come with it. If you are also weighing this against Railway for the rest of your stack, our [Railway alternative comparison](/blog/railway-alternative-2026) covers how that portability compares. ## FAQ ### Does Metabase need Postgres, or can I use the built-in database? Metabase ships with an embedded H2 file by default, which is not recommended once more than one person depends on it. Wiring a real Postgres database through `MB_DB_*`, which Ownkube's recipe does automatically, is the [production setup Metabase itself recommends](https://www.metabase.com/docs/latest/). ### How much memory does Metabase need on Ownkube? Plan on at least 1 GiB. Ownkube's recipe requests 1 GiB and caps at 2 GiB for the JVM's headroom; going below that risks crash loops during startup rather than a graceful slowdown. ### Is this the actual official Metabase image? Yes. The recipe pulls `metabase/metabase` directly from [Docker Hub](https://docs.docker.com/) at a pinned version, not a custom build. ### Do I create the admin account through Ownkube? No. Metabase's own setup wizard runs the first time you open the app, and that is where you create the admin account. ### Can Metabase still connect to other databases as data sources? Yes, and that is separate from the Postgres database Ownkube wires in for Metabase's own application data. You add any additional database, warehouse, or spreadsheet as a data source from inside Metabase once it is running. ## Where Ownkube fits If you want Metabase's dashboards and SQL editor without babysitting an H2 file or sizing a JVM host from scratch, the Marketplace recipe gets you the official image, a real Postgres database wired in from the start, and a TLS-covered URL in one action. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy a FastAPI app on Ownkube > Deploy a FastAPI app on Ownkube behind Gunicorn and Uvicorn workers, with a health check endpoint, an optional managed Postgres, and autoscaling under load. - Canonical: https://ownkube.io/blog/deploy-fastapi-app - Markdown: https://ownkube.io/blog/deploy-fastapi-app.md - Published: 2026-08-26 - Author: Abhishek Jaiswal - Category: How-To - Tags: fastapi, python, deploy, tutorial, ownkube-compute [FastAPI](https://fastapi.tiangolo.com/) is fast to run locally with `uvicorn app.main:app --reload`, and the production version of that command is not much longer. What changes is running behind a process manager that restarts a crashed worker, wiring config through environment variables instead of a `.env` file in the repo, and giving the platform a health check to poll. Ownkube handles the rest from a git push. This walks through deploying a FastAPI app on Ownkube Compute: running Gunicorn with Uvicorn workers, configuring settings through [environment variables](/blog/manage-secrets-env-vars), adding a health check endpoint, and turning on [autoscaling](/blog/autoscale-cpu-memory). **TL;DR** - Push a FastAPI app with a `Dockerfile`, and Ownkube builds it, deploys it, and issues TLS automatically on your `*.ownkube.app` hostname. - Run Gunicorn with `uvicorn.workers.UvicornWorker` in production rather than Uvicorn's dev server directly, so a crashed worker restarts instead of taking the app down. - Load configuration through a Pydantic `BaseSettings` class and set the real values as environment variables in the dashboard, never in the repo. - Add a Postgres database only if the app needs one; a stateless API can skip it entirely. - Expose a `/health` route and turn on horizontal autoscaling once traffic is uneven. ## 1. Structure the app for a container A typical FastAPI project layout works as-is; nothing about the layout is Ownkube-specific: ``` app/ __init__.py main.py config.py routers/ requirements.txt Dockerfile ``` Load configuration through `pydantic-settings` so every value has a typed default and a clear environment variable name: ```python # app/config.py from pydantic_settings import BaseSettings class Settings(BaseSettings): database_url: str | None = None environment: str = "production" cors_origins: list[str] = [] class Config: env_file = None # read only from real environment variables in production settings = Settings() ``` Add a health check route. It costs a few lines and gives Ownkube something reliable to check the container against: ```python # app/main.py from fastapi import FastAPI from app.config import settings app = FastAPI() @app.get("/health") def health(): return {"status": "ok"} ``` ## 2. Write a Dockerfile ```dockerfile FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . ENV PYTHONUNBUFFERED=1 EXPOSE 8000 CMD ["gunicorn", "app.main:app", \ "--worker-class", "uvicorn.workers.UvicornWorker", \ "--bind", "0.0.0.0:8000", \ "--workers", "4", \ "--timeout", "60"] ``` [Gunicorn](https://docs.gunicorn.org/) in front of [Uvicorn](https://www.uvicorn.org/) workers is the standard production pairing: Gunicorn manages the worker pool and restarts a worker that dies, and each worker runs Uvicorn's ASGI event loop. Running `uvicorn` directly with no process manager works for a demo, not for a container that needs to survive a bad request or an unhandled exception. Add `requirements.txt`: ``` fastapi uvicorn[standard] gunicorn pydantic-settings ``` ## 3. Push the repo and create the app Connect the repository from the Ownkube dashboard (`app.ownkube.io`) or push directly with the `okctl` CLI: ```bash okctl login okctl apps create fastapi-app --dockerfile Dockerfile okctl deploy ``` Ownkube builds the image, allocates a hostname on `*.ownkube.app`, and issues TLS automatically. If the repo has no Dockerfile, Ownkube's automatic build detects Python and builds an image for you, though a Dockerfile gives more control over the worker command above. ## 4. Add a managed Postgres database, if you need one Plenty of FastAPI services are stateless: they call out to another API and return a response, and nothing needs to persist. If that describes your app, skip this step. If the app does need a database, create a database box from the dashboard: **New > Database > Postgres**. It provisions a private (in-region) Postgres box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team. It runs as a single instance by default; opt-in public access and, on Team, high availability are per-database options. Attach the connection string as an environment variable and read it through settings: ```bash okctl env set DATABASE_URL="postgresql+asyncpg://user:password@db-host:5432/fastapi_production" --app fastapi-app ``` ```python # app/config.py class Settings(BaseSettings): database_url: str | None = None ``` With SQLAlchemy's async engine, point it straight at `settings.database_url`: ```python from sqlalchemy.ext.asyncio import create_async_engine engine = create_async_engine(settings.database_url, pool_pre_ping=True) ``` ## 5. Set your configuration as environment variables Anything the Pydantic `Settings` class reads should come from the dashboard, not a committed `.env` file: ```bash okctl env set ENVIRONMENT=production --app fastapi-app okctl env set CORS_ORIGINS="https://yourapp.com" --app fastapi-app ``` Environment variables set through Ownkube are encrypted at rest and only injected into the running container, so secrets like API keys or a database password never live in the repo or the image. ## 6. Scale and watch it run A fresh FastAPI service usually starts fine on a small metered ceiling: ```bash okctl deploy --cpu-limit 500m --memory-limit 512Mi ``` Turn on horizontal autoscaling once traffic is uneven, so Ownkube adds Uvicorn worker instances under load and scales back down when it is quiet: ```bash okctl apps update fastapi-app --autoscale-min 1 --autoscale-max 5 --autoscale-cpu-target 70 ``` Metered billing suits an API that idles between bursts: it charges CPU and memory on actual per-minute usage, so a service that sits quiet overnight barely draws the wallet down. If the service instead runs hot around the clock, for example a worker consuming a queue continuously, a flat Spark or Core box is a steadier price than metering a constant load. Logs and metrics for the app live in the dashboard, so a worker that keeps restarting or a slow endpoint shows up in one place. ## Moving to your own AWS later If the service needs to run inside a customer's AWS account, or you outgrow the shared Compute tier, the same Dockerfile and FastAPI app move to your own AWS with no rewrite. Ownkube provisions the instance, wires up the same deploy flow, and brings the logs, metrics, and health checks with it, which is a path a platform like Railway does not offer since it only runs on its own infrastructure. Our [Railway alternative comparison](/blog/railway-alternative-2026) goes into where that matters most. ## FAQ ### Should I use Uvicorn directly or Gunicorn with Uvicorn workers? Use Gunicorn with `uvicorn.workers.UvicornWorker` in production. Gunicorn manages the worker pool, restarts a worker that crashes, and gives you a `--workers` count to tune; running Uvicorn alone has none of that supervision. ### Does Ownkube support WebSockets? Yes, a Uvicorn worker handles WebSocket connections the same way it does in any other deployment. Keep in mind that autoscaling adds new instances rather than new connections to an existing one, so a long-lived WebSocket workload should size `--workers` and the autoscale ceiling with connection count in mind, not just CPU. ### How does Ownkube know my app is healthy? Ownkube watches the container and restarts it if the process stops responding. Exposing a lightweight `/health` route that does not touch the database gives it a fast, reliable signal, separate from a `/status` endpoint that might check dependencies and take longer to answer. ### What happens if my wallet balance runs out mid-month? The app pauses and any attached database keeps its data. Adding credit or the next monthly plan load resumes it. Unused wallet credit rolls over and never expires. ### Can I run a background worker alongside the API? Yes. Deploy it as a second Ownkube app or as a [scheduled job](/blog/run-scheduled-cron-jobs) if the work runs on a cadence rather than continuously, and point it at the same Postgres connection string if it shares data with the API. **Reference:** For the always-current version of this walkthrough, see the [Deploy FastAPI](/docs/deploy/fastapi) guide in the docs. ## Where Ownkube fits FastAPI wants a process manager that survives a crashed worker, config that stays out of the repo, and a place to add a database only when the app actually needs one, and Ownkube gives you all three without provisioning a cloud account first. Push the Dockerfile, set your environment variables, and turn on autoscaling when traffic gets uneven. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy any Docker image on Ownkube > Deploy a containerized app on Ownkube from a Dockerfile: set the port and env vars, configure a health check, and pick metered or reserved pricing. - Canonical: https://ownkube.io/blog/deploy-docker-image - Markdown: https://ownkube.io/blog/deploy-docker-image.md - Published: 2026-08-25 - Author: Abhishek Jaiswal - Category: How-To - Tags: docker, container, deploy, tutorial, ownkube-compute If your app already runs in a container, Ownkube builds it from your Dockerfile and runs it with a public URL, TLS, health checks, and [autoscaling](/blog/autoscale-cpu-memory) attached, no Kubernetes manifests to write. This walks through deploying a Dockerfile-based app end to end: connecting the repo, setting the port and environment variables, adding a health check, and choosing between metered and reserved pricing. One thing worth being upfront about: today the shipped path to a running container is a Dockerfile in a connected GitHub or GitLab repository, which Ownkube builds and pushes to a managed registry for you. Pulling a prebuilt image straight from an external registry like Docker Hub or a bare ECR repo is on the roadmap, not available yet. If your image already exists somewhere else, put its Dockerfile (or the source it's built from) in a repo and point Ownkube at that instead. In practice that covers the large majority of "deploy this container" needs: anything you can express as a Dockerfile, Ownkube can build and run. **TL;DR** - Connect a GitHub or GitLab repo with a Dockerfile. Ownkube builds the image and pushes it to a managed ECR it runs for you, no registry account of your own required. - If the repo has more than one Dockerfile, you pick which one at deploy time. If it has none, Ownkube's automatic build detects the language and builds it for you instead. - Set the port your container listens on, add environment variables, and point a health check at a real endpoint before you call it done. - Every deployment gets an auto-allocated `*.ownkube.app` hostname with automatic TLS the moment it's live. - Choose metered pricing (bills on actual CPU and memory usage, cheapest for anything that idles) or a flat reserved box (predictable price, better for a workload that runs hot around the clock), per deployment. - Pulling a prebuilt image from an external registry isn't shipped yet. Deploying from a Dockerfile in a repo is the way to get any containerized app running today. ## What "any Docker image" means on Ownkube right now A [Dockerfile](https://docs.docker.com/reference/dockerfile/) is a complete, portable recipe for an image: `FROM`, your dependencies, your `COPY`, your `CMD`. If you can [build an image locally](https://docs.docker.com/build/building/best-practices/) with `docker build`, that same Dockerfile is what Ownkube needs, it runs the identical build server-side and pushes the result to the managed registry it manages for you. That's true whether the container underneath is a Node API, a Go binary, a Python worker, or a piece of open-source software you're self-hosting; if it builds from a Dockerfile, it deploys the same way. What isn't available yet is connecting your own existing registry (Docker Hub, a bare ECR repository, GCR, ACR) and pointing a deployment at an image that's already sitting there. The registry-connect screen exists in the UI as a "coming soon" placeholder for every provider. Until that ships, get the image's Dockerfile into a repo Ownkube can build from. For most self-hosted tools this is trivial, the project's own repo usually already has one. ## Before you start - A GitHub or GitLab repository with a working Dockerfile (or no Dockerfile at all, if you want Ownkube's automatic build to detect the language for you). - The port your container listens on internally. - Any environment variables or secrets the app needs at runtime. ## Step 1: connect your repository From the Ownkube dashboard, start a new deployment and connect the repository if you haven't already. GitHub goes through the GitHub App (installable on more than one account or organization per Ownkube org, so a personal repo and a work org can both connect); GitLab goes through a direct account connection. Personal access tokens work for either if you'd rather not install an app. ## Step 2: choose the build target ```text Deploy new -> Web service Source: your-org/your-repo Branch: main Build target: Dockerfile (api/Dockerfile) ``` If the repo has a Dockerfile, Ownkube builds from it, and if there's more than one, you choose which. If there's none, the deploy flow switches to **Auto**, Ownkube detects the language and builds the image itself, no Dockerfile required. ## Step 3: set the port your container listens on Tell Ownkube which port your container's process binds to internally. This is the one setting a containerized deploy can't skip, get it wrong and the health check and public hostname both fail to find your app. ```text Container port: 8080 ``` ## Step 4: add environment variables Set the [environment variables](/blog/manage-secrets-env-vars) your container reads at runtime on the deployment's **Environment** tab. ```text DATABASE_URL=postgres://... NODE_ENV=production LOG_LEVEL=info ``` Variables are set per deployment. If you're running the same image in more than one environment (staging and production, for instance), set them again in each; there's no shared, environment-wide variable set yet. ## Step 5: configure a health check On the deployment's **Settings** tab, under **Health checks**, point the readiness and liveness probes at a real endpoint your container serves. ```text Health check path: /health Initial delay: 5s Timeout: 3s ``` Without one, Ownkube can only infer that the container process is running, not that it's actually able to serve a request. A health check is what keeps a slow-starting database migration from getting real traffic before it's ready. ## Step 6: choose metered or reserved pricing Every deployment on Ownkube Compute picks one of two billing modes, and you can switch later. **Metered** is the default and fits most containerized apps: you set CPU and memory limits as ceilings, and the bill follows actual usage, both CPU and memory, per minute. A container that idles most of the day (a small API, a side project) costs almost nothing on the CPU line while it's quiet. **Reserved** puts the deployment on a fixed-size box, a Spark box (shared CPU, from $5 a month) or a Core box (dedicated CPU, from $58 a month), that bills a flat rate whether it's busy or idle. Pick this for a container that runs hot continuously, a queue worker that's always processing something, where the flat price is more predictable than watching a meter. Both modes draw from the same prepaid wallet, so switching a deployment from metered to reserved (or back) later doesn't change how you pay, only what you're billed for. ## Step 7: deploy and check the public URL Once the build finishes and the health check passes, the deployment's **Overview** tab shows its public hostname, an auto-allocated `*.ownkube.app` domain with automatic TLS issued the moment it goes live. No certificate to request, no DNS record to add for this default hostname. ```text https://your-app-a1b2c3.ownkube.app ``` [Custom domains](/blog/automatic-https-custom-domain) (pointing your own domain at the app) are on the roadmap for Ownkube Compute specifically; they're shipped today on the own-cloud (BYOC) path if you're running on your own AWS account instead. ## What happens on the next push Every subsequent push to the connected branch triggers a new build and a zero-downtime rollout, the old container keeps serving traffic until the new one passes its health check, then traffic switches over. If a build or rollout goes wrong, roll back to the previous revision from the deployment's Revisions tab. ## Moving the same container to your own AWS later Nothing about deploying from a Dockerfile locks you into Ownkube-managed compute specifically. The same app, built the same way, can move to your own (or a customer's) AWS account later with no rewrite, and its logs, metrics, and health checks come with it. That's the option to have in your pocket if a workload eventually needs to sit inside a specific AWS account rather than Ownkube's shared compute. If you're weighing that decision now, our [comparison of Fly.io and running your own AWS](/blog/fly-io-alternative-own-aws) walks through when that tradeoff is worth making. For teams evaluating platforms broadly rather than just this one workflow, [Railway vs Render vs Northflank](/blog/railway-vs-render-vs-northflank-2026) is a useful side-by-side if Docker deploys are one requirement among several you're comparing. ## FAQ ### Can I deploy a prebuilt Docker image straight from Docker Hub or ECR? Not yet. Connecting an external registry is on the roadmap and shows as a "coming soon" stub in the UI today. The shipped path is a [Dockerfile](https://docs.docker.com/) in a connected GitHub or GitLab repo, Ownkube builds it and pushes to a managed registry for you. ### Does my repo need a Dockerfile? No. If it has one, Ownkube builds from it. If it doesn't, Ownkube's automatic build detects the language and builds the image for you, no Dockerfile required either way. ### What if my repo has more than one Dockerfile? You pick which one at deploy time, useful for a monorepo with separate services under different paths. ### How do I choose between metered and reserved pricing? Metered fits a container that idles some of the time, since CPU cost drops close to zero while it's quiet. Reserved fits a container that runs continuously at similar load, since a flat box gives you a price that doesn't move. You choose per deployment and can switch later. ### Does the deployment get HTTPS automatically? Yes. Every deployment gets an auto-allocated `*.ownkube.app` hostname with automatic TLS the moment it's live, no certificate request or DNS record needed for that default hostname. **Reference:** For registry setup and image pull details, see the [Registries](/docs/features/registries) reference in the docs. ## Where Ownkube fits If your app is already containerized, getting it running is a Dockerfile, a port, and a health check away, with a live TLS URL attached before you've thought about a certificate. The one gap to plan around today is pulling an image straight from an external registry; until that ships, a Dockerfile in a repo gets you the same result for almost any container. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy an Express / Node.js API on Ownkube > Deploy an Express or plain Node.js API on Ownkube with npm start, a PORT env var, an optional managed Postgres, a health check, and autoscaling. - Canonical: https://ownkube.io/blog/deploy-express-node-api - Markdown: https://ownkube.io/blog/deploy-express-node-api.md - Published: 2026-08-25 - Author: Abhishek Jaiswal - Category: How-To - Tags: express, nodejs, deploy, tutorial, api An [Express](https://expressjs.com/) API needs almost nothing from a host beyond a process that runs `npm start`, a port it can bind to, and somewhere to send traffic once it is healthy. Ownkube handles all of that from a git push, with a managed Postgres available if the API needs one. This walks through deploying a [Node.js](https://nodejs.org/en/docs) Express API on Ownkube Compute: the start command, the `PORT` environment variable, an optional database, a health check endpoint, and [horizontal autoscaling](/blog/autoscale-cpu-memory). **TL;DR** - Ownkube runs `npm start` (or your Dockerfile's `CMD`) and expects the app to bind to `process.env.PORT`. - Add a managed Postgres if the API needs a database, and pass its connection string in as `DATABASE_URL`. - Add a `/health` route so Ownkube's health checks can tell a live process from a stuck one. - Turn on horizontal autoscaling so the API adds instances under load and idles back down when quiet. - Metered billing suits most APIs: a mostly-idle service between requests barely registers on the CPU meter. ## 1. Confirm the app binds to the right port Ownkube injects a `PORT` environment variable into the container. The app has to read it rather than hardcoding a port: ```js // server.js const express = require("express"); const app = express(); app.get("/health", (req, res) => { res.status(200).json({ status: "ok" }); }); app.get("/", (req, res) => { res.json({ message: "hello from Ownkube" }); }); const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`listening on ${port}`); }); ``` Make sure `package.json` has a `start` script that Ownkube can run directly: ```json { "scripts": { "start": "node server.js" } } ``` ## 2. Add a Dockerfile (optional, recommended for control) You can deploy straight from source, but a Dockerfile gives you a pinned Node version and a smaller runtime image: ```dockerfile FROM node:20-slim AS build WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=dev FROM node:20-slim WORKDIR /app COPY --from=build /app/node_modules ./node_modules COPY . . ENV NODE_ENV=production EXPOSE 3000 CMD ["npm", "start"] ``` ## 3. Push and create the app Connect the repo from the dashboard, or use `okctl`: ```bash okctl login okctl apps create express-api --dockerfile Dockerfile okctl deploy ``` Without a Dockerfile, Ownkube can build straight from `package.json` and run `npm start`, which is enough for a simple API with no native dependencies. Ownkube allocates a `*.ownkube.app` hostname with automatic TLS, so the API is reachable over HTTPS the moment it deploys. ## 4. Set environment variables Set any config the API needs, including secrets, from the dashboard or the CLI. These are injected into the running container and never committed to the repo: ```bash okctl env set NODE_ENV=production --app express-api okctl env set API_KEY="your-secret-key" --app express-api ``` ## 5. Add a managed Postgres, if the API needs one Not every API needs a database, but if yours does, create a [Postgres box](/blog/add-managed-postgres-database) from **New > Database > Postgres** in the dashboard. It is a private (in-region) box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team. It runs as a single instance by default; opt-in public access and, on Team, high availability are per-database options. ```bash okctl env set DATABASE_URL="postgres://user:password@db-host:5432/api_production" --app express-api ``` With something like `pg` or an ORM such as [Prisma](https://www.prisma.io/docs) or Drizzle, point the client straight at `process.env.DATABASE_URL`: ```js const { Pool } = require("pg"); const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` ## 6. Wire up the health check Ownkube uses a health check endpoint to know when a new deploy is actually ready to take traffic, and to detect a process that is alive but stuck. Point it at the `/health` route from step 1: ```bash okctl apps update express-api --health-check-path /health ``` A deploy only completes the rollout, and an old instance only gets terminated, once the new instance's health check passes. That is what makes rollouts zero-downtime instead of a race between old and new processes. ## 7. Turn on autoscaling Most APIs see uneven traffic through the day. Horizontal autoscaling adds instances when CPU or memory crosses a threshold and scales back down when the API is quiet: ```bash okctl apps update express-api --autoscale-min 1 --autoscale-max 5 --autoscale-cpu-target 70 ``` Combined with metered billing, an API that is busy for an hour a day and idle the rest of the time pays close to nothing on CPU during the idle hours, since metered pricing bills both CPU and memory on what is actually used, not on the instance size. ```bash okctl deploy --cpu-limit 500m --memory-limit 512Mi ``` ## 8. Check logs and metrics Logs and per-instance metrics are in the Ownkube dashboard as soon as the app is deployed, which is usually where a bad health check or a crash loop shows up first, before a user reports it. ## Moving to your own AWS later If the API needs to run inside a specific AWS account, either your own or a customer's, for compliance or contractual reasons, the same Dockerfile moves over with no rewrite. Ownkube provisions the instance in that AWS account and keeps the same deploy flow, logs, metrics, and health checks attached. Railway does not offer that path since it only runs on Railway's own infrastructure. Our [Railway alternative comparison](/blog/railway-alternative-2026) goes through what changes and what does not when you make that move. ## FAQ ### Does Ownkube support WebSockets? Yes. A standard Express app using `ws` or `socket.io` on the same HTTP server works without extra configuration, since Ownkube proxies the app's single port. ### How do I run database migrations for a Node API? Set a release command, the same way you would run any one-off command before a deploy completes, for example `npx prisma migrate deploy` or `npx knex migrate:latest`: ```bash okctl apps update express-api --release-command "npx prisma migrate deploy" ``` ### Can I run a background worker alongside the API? Yes. Deploy the worker as a separate Ownkube app that shares the same `DATABASE_URL`, and optionally a [Valkey cache box](/blog/add-valkey-redis-cache) (from $2 a month) if the worker needs a queue. ### What happens if I forget to set PORT? You do not need to set `PORT` yourself. Ownkube injects it into the container, and the app should read `process.env.PORT` rather than hardcoding a value, as shown in step 1. ### Is there a free tier for small APIs? No, Compute has no free tier, but you don't need a subscription either: top up from $5 and pay per minute. The Personal plan is $5 a month and loads $5 of wallet credit you then spend, so the plan price is also usable balance. Either way, unused credit rolls over and never expires. **Reference:** For the always-current version of this walkthrough, see the [Deploy Express](/docs/deploy/express) guide in the docs. ## Where Ownkube fits An Express API mostly needs a place to run `npm start`, a port to bind, and a health check that tells the platform when it is actually ready, and Ownkube covers that without a cloud account or a YAML pipeline to write first. Add a managed Postgres if the API needs state, turn on autoscaling if traffic is uneven, and the rollout stays zero-downtime by construction. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## Railway vs Render vs Northflank in 2026 > Comparing the three app platforms developers evaluate most in 2026: pricing, billing, where your app runs, the buyer each fits, plus Ownkube. - Canonical: https://ownkube.io/blog/railway-vs-render-vs-northflank-2026 - Markdown: https://ownkube.io/blog/railway-vs-render-vs-northflank-2026.md - Published: 2026-08-25 - Author: Abhishek Jaiswal - Category: Engineering - Tags: railway, render, northflank, paas-comparison, heroku-alternative, pricing Railway, Render, and Northflank are the three app platforms we see developers put side by side most often in 2026. All three will take a container or a git repo and give you a running URL, so the demo looks the same on every one. The differences that matter show up a month later: on the invoice, in how the bill is calculated, and in what happens when the project you shipped as a side project turns into something you actually depend on. This post is a buyer's comparison, not a scoreboard. We'll cover what each platform is good at, how each one prices, and the buyer each one is really built for. Then we'll be straight about where Ownkube fits and where it doesn't. If you're coming from Heroku specifically, our [Heroku pricing and alternatives guide](/blog/heroku-2026-pricing-alternatives) covers that migration in more detail. ## The short version - [**Railway**](https://docs.railway.com/) has the best raw developer experience of the three, and it bills purely on metered usage by the second. The catch is that the metered model is postpaid, so a service you forget about keeps spending, and the once-generous free tier is effectively gone (a dollar of credit a month). - [**Render**](https://render.com/docs) is the most predictable and the most Heroku-like. You pick a fixed instance size and pay a flat rate for it. The catch is that you pay for that instance whether it's busy or idle, and Render is drifting upmarket (a new [$499-a-month Scale plan](https://render.com/pricing), SSO, SCIM, HIPAA). - [**Northflank**](https://northflank.com/docs) is the most powerful and the only one of the three with real bring-your-own-cloud. It's also the cheapest on raw CPU. The catch is that it still puts Kubernetes in front of you (its own tagline is "making Kubernetes boring"), and it's built for platform teams more than for one developer with a side project. - **Ownkube** is for personal software and small teams: a prepaid wallet so nothing surprises you, a choice of metered or flat pricing per app, and a real path into your own AWS account with no rewrite. It's the least suited of the group if you want a single throwaway VPS or GPU training runs. The rest of this post is the detail behind that call. ## How they price (the structure, not the number) The prices below move, so treat them as the shape of each model rather than a quote. The structural differences are the part that stays true. | | Railway | Render | Northflank | Ownkube | |---|---|---|---|---| | Pricing structure | Metered usage, per second | Fixed instance sizes + a workspace plan | Metered usage per vCPU-hour (fixed plans optional) | Metered or a flat-price box, your choice per app | | Billing method | Plan credit, then postpaid to your card | Postpaid to your card | Postpaid pay-as-you-go | Prepaid wallet the plan loads and usage drains | | Entry plan | Hobby $5/mo (includes $5 usage) | Hobby workspace $0, small instance from about $7/mo | Sandbox free, then pay-as-you-go | Pay as you go (top up from $5), Personal $5/mo, Team $20/mo | | Free tier | About $1 of credit a month | Real, but web services spin down and a free Postgres expires in 30 days | Sandbox with a couple of always-on services | A permanent free Starter in your own AWS (you pay AWS for the instance, from about $30/mo) | | Own cloud (BYOC) | No | No | Yes, multi-cloud, no added platform cost in your VPC | Yes, AWS today, and no markup on AWS compute | | Egress | About $0.05/GB | About $0.15/GB over the included amount | About $0.06/GB | $0.02/GB (Germany) to $0.13/GB (Mumbai), outbound only | Three different philosophies sit in that table. Railway meters everything and bills you after. Render sells you a fixed box and bills you a flat rate for it. Northflank meters like Railway but will also run inside your own cloud account. The thing all three share is that the spend is postpaid: the meter runs, or the box exists, and the invoice arrives at the end. ## The four things that actually differ ### 1. Prepaid wallet vs the surprise invoice This is the difference most people feel first, because it's the one that shows up as a bad morning. Railway and Northflank are postpaid metered, which is the model behind every "I left a service running and got a four-figure bill" story. Render hides the same problem in a different shape: you pay for the instance while it sleeps, so the waste is quiet instead of loud. Ownkube runs on a prepaid wallet. Your plan loads credit each month (Personal loads $5, Team loads $20), and usage draws it down per minute. When the balance reaches zero, your apps pause and your data stays put until you top up or the next monthly load arrives. Nothing runs up a card you weren't watching. For personal software and small teams, spending being bounded by a number you chose on purpose is worth more than shaving a cent off the per-hour rate. ### 2. Metered or flat, and you pick per app Railway only meters. That's great for an app that idles most of the day and painful for a busy one, because the number keeps moving. Render only does fixed sizes. That's predictable and it means you pay for idle. Both models are fine; the problem is that each platform forces one on you for everything you run. Ownkube lets you choose per deployment. Run a bursty web app metered so an idle hour costs almost nothing, and put a steady worker on a flat Spark or Core box so its price never moves, in the same project, and flip either one later. Metered CPU and memory both bill on what the app actually uses, and a managed web app can [scale to zero](/blog/vercel-alternatives-backend-services-2026) when idle so it stops metering until the next request wakes it. Databases and caches are always a flat box (a database box from $4 a month plus $0.25 per GB of storage, a Valkey cache box from $2), because those hold memory and run continuously either way. ### 3. Where your app is allowed to run Railway and Render run your app on their infrastructure, full stop. There is no version of either one where the same app runs in an AWS account you own. For a weekend project that's fine. For anything a customer's security team asks about, or anything you want to keep after you leave the platform, it's a wall. Northflank does have bring-your-own-cloud, and it's a genuine strength, so if multi-cloud portability is your main requirement, give it a serious look. The difference is who it's aimed at: Northflank's own-cloud story is built for platform teams standing up infrastructure. Ownkube's is built for one developer. The same app you ship on Ownkube-managed compute moves into your own (or a customer's) AWS account with no rewrite, and its logs, metrics, and health checks come with it. On your own cluster you pay AWS directly at wholesale (no platform markup on EC2 or storage), AWS Activate credits apply in full, and you can disconnect anytime and your apps keep running. That's portability you can hand to a customer, not just a config option. Our [Fly.io alternative post](/blog/fly-io-alternative-own-aws) walks through the own-cloud path in more depth. ### 4. Who the platform is actually built for This one is easy to miss because it hides in the roadmap rather than the pricing page. Railway removed its free tier and is pushing toward Pro and Enterprise. Render just added a $499-a-month Scale plan with SSO, SCIM, and HIPAA eligibility. Both are healthy moves for those companies, and both point the same direction: upmarket, toward platform and compliance buyers. Northflank was aimed at platform teams from the start. The developer shipping a real side project, or the team of three that wants to look like a bigger company without hiring for it, is being quietly left behind by two of the three. That's the buyer Ownkube is built for. "A cloud for personal software" is the whole thesis, not a tier we tolerate. ## An honest note on raw price If your only question is the lowest possible dollar-per-vCPU, Ownkube is not always the answer, and we'd rather say so than pretend. Northflank's metered CPU works out cheaper than ours per vCPU. Railway's per-second CPU lands close to ours. Where Ownkube wins on the number is memory: our rate per GiB is well under Railway's, and personal software tends to be memory-heavy and CPU-idle, so the app sits on the memory line most of the day. A typical always-on app of that shape comes in under the equivalent Railway bill once you add both lines together. But raw rate is the wrong thing to optimize for at this size. The money you actually lose on a small platform is the forgotten service that metered all month, the fixed instance that sat idle, or the migration you have to pay for when you outgrow a platform your app can't leave. Structure beats rate at this scale, which is why we lead with the wallet and the own-cloud path instead of a price war. ## Now price the whole app, not the vCPU The per-vCPU argument misses the point because nobody ships a bare web process. A real app is a web service plus a managed Postgres plus a cache, and that is where the comparison actually lands. Railway, Render, and Northflank all bill the database and the cache as separate line items with a markup over the raw compute. Ownkube meters the app per minute and runs Postgres and the cache on near-cost boxes (a database box from $4 a month plus $0.25 per GB of storage, a Valkey cache box from $2), so the parts that run all day are the parts where we are cheapest. Price one representative stack: a small always-on web app (1 vCPU, 1 GiB), a managed Postgres (1 GiB RAM, 10 GB), and a small Redis-compatible cache. | Monthly cost, full stack | Railway | Render | Northflank | Ownkube | |---|---|---|---|---| | Web app (1 vCPU / 1 GiB, always on) | ~$18 to $30 | ~$25 | ~$18 | ~$12 | | Managed Postgres (1 GiB / 10 GB) | ~$18 | ~$22 | ~$14 | ~$11 | | Cache (small Redis or Valkey) | ~$5 to $8 | ~$10 | ~$5 | ~$5 | | **Total** | **~$44 to $56** | **~$57** | **~$37** | **~$27** | Treat the numbers as illustrative: competitor plans are mapped to the nearest real tier from their published pricing as of October 2026, and the metered platforms (Railway and Ownkube) move with how busy the app is, so an idle app costs Ownkube less than the figure above and a CPU-hot one costs more. Northflank stays the closest of the three, which is fair: its metered compute is genuinely cheap. The gap comes from the database and the cache, not a per-vCPU trick. You can run the same comparison with your own sizes on the [homepage calculator](/#pricing-compare). ## Where Ownkube fits Pick Ownkube if the following sound like you: - You want to ship an app or a database in seconds with no cloud account to set up, and you want the bill to be a balance you loaded, not a surprise. - You want to choose predictable flat pricing or pay-for-what-runs metering per app, instead of having one model forced on everything. - You expect that at some point the app will need to live in your own AWS account (a customer requirement, compliance, or credits), and you don't want that to mean a rewrite. - You're one developer or a small team, and you'd rather the platform handle TLS, managed Postgres, a Valkey cache, autoscaling, logs, metrics, and zero-downtime rollbacks than hire someone to. Pick something else if you want a single $5 VPS for a throwaway project (a self-hosted option is simpler, see our [self-hosted PaaS comparison](/blog/self-hosted-paas-comparison-2026)), if you need GPU training runs today (Northflank), or if raw dollar-per-vCPU is the only axis you care about (Northflank again). **Reference:** For the side-by-side breakdown, see [Ownkube vs Render](/docs/compare/render) in the docs. ## Closing Railway, Render, and Northflank are all good platforms, and each earns its place: Railway for developer experience, Render for predictability, Northflank for power and multi-cloud. The gap they share for personal software is the invoice model. Two of them can surprise you, one makes you pay for idle, and none of the three lets a small team keep the same app when they move it into their own cloud. That's the shape Ownkube takes. Ship in seconds on Ownkube Compute from $5 a month with nothing to set up, [load a wallet instead of exposing a card](/blog/prepaid-wallet-no-surprise-bill), and run in your own cloud when you're ready: free on a Starter instance, $5 per vCPU plus $1 per GiB RAM at Production. [Deploy your first app](https://app.ownkube.io/login). --- ## How to deploy a Django app on Ownkube > Deploy a Django app on Ownkube with Gunicorn, a managed Postgres database, WhiteNoise for static files, and migrations that run automatically on release. - Canonical: https://ownkube.io/blog/deploy-django-app - Markdown: https://ownkube.io/blog/deploy-django-app.md - Published: 2026-08-24 - Author: Abhishek Jaiswal - Category: How-To - Tags: django, python, deploy, tutorial, ownkube-compute A [Django](https://docs.djangoproject.com/en/stable/) app in production needs four things from a host: a real WSGI server in front of it, a Postgres database it can reach, static files served correctly, and migrations that run before the new code takes traffic. Ownkube covers all four from a git push, with no cloud account and no YAML to hand-write. This walks through deploying a standard Django app on Ownkube Compute: swapping the dev server for Gunicorn, wiring up a [managed Postgres](/blog/add-managed-postgres-database), handling static files with WhiteNoise, and running `migrate` on release. **TL;DR** - Push a Django app with a `Dockerfile`, and Ownkube builds it, deploys it, and issues TLS automatically on your `*.ownkube.app` hostname. - Add an Ownkube-managed Postgres database and point `DATABASE_URL` at it with `dj-database-url` or the equivalent. - Serve static files with WhiteNoise instead of standing up a separate static host, and run `collectstatic` in the Docker build. - Set `SECRET_KEY`, `DJANGO_SETTINGS_MODULE`, and `ALLOWED_HOSTS` as environment variables in the dashboard, not in the repo. - Run `migrate` as a release step so it happens once, before Gunicorn starts serving the new version. ## 1. Prepare settings for production Most of this is standard Django, not anything Ownkube-specific. Point the database and static file settings at environment variables instead of hardcoded values: ```python # settings.py import os import dj_database_url DEBUG = False SECRET_KEY = os.environ["SECRET_KEY"] ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",") DATABASES = { "default": dj_database_url.config(env="DATABASE_URL", conn_max_age=600) } STATIC_URL = "/static/" STATIC_ROOT = "/app/staticfiles" STORAGES = { "staticfiles": { "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", }, } MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", # ...the rest of your middleware ] ``` Add `whitenoise`, `gunicorn`, `psycopg[binary]`, and `dj-database-url` to `requirements.txt`. [WhiteNoise](https://whitenoise.readthedocs.io/) means you do not need a separate static file host or CDN just to serve CSS and JS in front of a small app. ## 2. Write a Dockerfile Ownkube deploys from a Dockerfile when the repo has one, so you get full control over the Python version and the build steps: ```dockerfile FROM python:3.12-slim RUN apt-get update -qq && apt-get install -y libpq-dev build-essential \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # Collect static files at build time, before SECRET_KEY exists at runtime RUN SECRET_KEY=build-only DATABASE_URL=sqlite:///build.db \ python manage.py collectstatic --noinput ENV PYTHONUNBUFFERED=1 EXPOSE 8000 CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"] ``` [Gunicorn](https://docs.gunicorn.org/)'s worker count is a starting point. A CPU-bound app usually wants workers close to `(2 x vCPU) + 1`; an I/O-bound app that mostly waits on the database can run more workers per vCPU than that rule suggests. ## 3. Push the repo and create the app Connect the repository from the Ownkube dashboard (`app.ownkube.io`) or push directly with the `okctl` CLI: ```bash okctl login okctl apps create django-app --dockerfile Dockerfile okctl deploy ``` Ownkube builds the image, allocates a hostname on `*.ownkube.app`, and issues TLS automatically. No load balancer or certificate to configure by hand. ## 4. Add a managed Postgres database Create a database box from the dashboard: **New > Database > Postgres**. It provisions a private (in-region) Postgres box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team. It runs as a single private instance by default, which is the normal shape for a small to mid-size Django app; opt-in public access and, on Team, high availability are per-database options when you need them. Attach the connection string as an environment variable: ```bash okctl env set DATABASE_URL="postgres://user:password@db-host:5432/django_production" --app django-app ``` `dj_database_url.config()` in `settings.py` reads it directly, so there is no further Django-side configuration. ## 5. Set your secrets Django needs `SECRET_KEY` to sign sessions and cookies, and it will refuse to run with `DEBUG=False` and an empty `ALLOWED_HOSTS`. Set these as [environment variables](/blog/manage-secrets-env-vars) in the dashboard rather than committing them: ```bash okctl env set SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(50))')" --app django-app okctl env set DJANGO_SETTINGS_MODULE=myproject.settings --app django-app okctl env set ALLOWED_HOSTS="django-app.ownkube.app" --app django-app ``` Never commit `SECRET_KEY` to the repository. Environment variables set through Ownkube are encrypted at rest and only injected into the running container. ## 6. Run migrations on release Migrations need to run before Gunicorn starts serving requests against the new code, not after. Configure `migrate` as a release command so Ownkube runs it once per deploy, ahead of the rollout: ```bash okctl apps update django-app --release-command "python manage.py migrate --noinput" ``` With a release command set, every `okctl deploy` runs the migration against the connected Postgres box first, and only rolls Gunicorn workers over to the new image once it succeeds. If the migration fails, the previous version keeps serving traffic, which is the zero-downtime rollout behavior working in your favor rather than a race between old code and a half-migrated schema. ## 7. Scale and watch it run A fresh Django app usually starts fine on a small metered ceiling: ```bash okctl deploy --cpu-limit 500m --memory-limit 1Gi ``` Turn on [horizontal autoscaling](/blog/autoscale-cpu-memory) if traffic is uneven, so Ownkube adds Gunicorn instances under load and scales back down when it is quiet: ```bash okctl apps update django-app --autoscale-min 1 --autoscale-max 4 --autoscale-cpu-target 70 ``` Metered billing fits a typical Django app well: it charges CPU and memory on actual per-minute usage, and a Django app sitting between requests barely registers on the CPU line. Logs and metrics for the app and the database box both live in the dashboard, so a slow migration or a worker that keeps restarting shows up in one place. ## Moving to your own AWS later If the app outgrows the shared Compute tier, or a client's security review asks where the app runs, the same Dockerfile and Django app move to your own AWS account with no rewrite. Ownkube provisions the instance, wires up the same deploy flow, and brings the logs, metrics, and health checks with it. That portability is one of the things that separates Ownkube from a platform like Railway, which only runs your workload on its own infrastructure. Our [Railway alternative comparison](/blog/railway-alternative-2026) covers the tradeoffs in more depth. ## FAQ ### Does Ownkube support Django's admin site and management commands? Yes. The admin works like any other Django view once `ALLOWED_HOSTS` and static files are configured. One-off management commands can run as a scheduled job triggered manually from the dashboard's Runs tab, rather than exec'ing into a running web container. ### Do I need a Dockerfile, or can Ownkube detect Django automatically? A Dockerfile gives you control over the Python version, system packages like `libpq-dev`, and the `collectstatic` build step, and is the most reliable path for Django. Simpler apps without native dependencies can also deploy straight from source using Ownkube's automatic build. ### Can I run Celery or another background worker alongside the web app? Yes. Deploy the worker as a second Ownkube app pointed at the same Postgres connection string, and add a Valkey cache box from $2 a month if you need Redis as the broker, so the web app and worker scale independently. ### What happens if my wallet balance runs out mid-month? The app pauses and its data, including the Postgres box, is kept. Adding credit or the next monthly plan load resumes it. Unused wallet credit rolls over and never expires, so a quiet month is not credit lost. ### Can I roll back a bad deploy? Yes. Ownkube keeps prior image versions and supports zero-downtime rollback from the dashboard or `okctl`, so a bad migration or a broken settings change does not require a manual recovery. **Reference:** For the always-current version of this walkthrough, see the [Deploy Django](/docs/deploy/django) guide in the docs. ## Where Ownkube fits Django wants a real database, a release step for migrations, and a place to serve static files without standing up a separate CDN, and Ownkube gives you all three without provisioning a cloud account first. Push the Dockerfile, attach a managed Postgres, set your secrets, and the release command handles the rest. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to self-host Cal.com on Ownkube > Deploy Cal.com's official image on Ownkube with a managed Postgres database, the required auth and encryption keys, and an HTTPS address for scheduling. - Canonical: https://ownkube.io/blog/deploy-cal-com - Markdown: https://ownkube.io/blog/deploy-cal-com.md - Published: 2026-08-23 - Author: Abhishek Jaiswal - Category: How-To - Tags: cal-com, self-hosting, scheduling, deploy, tutorial Cal.com is an open source scheduling tool built to be self-hosted, and running it yourself avoids the per-seat pricing of the hosted version once a team grows past a couple of people. This walks through deploying Cal.com's official [Docker image](https://docs.docker.com/) on Ownkube with Postgres, the auth secrets it requires, and a stable HTTPS address. **TL;DR** - Cal.com needs Postgres as its primary datastore. There is no SQLite option, so provision a managed database before deploying the app. - Deploy the official Cal.com image, set `DATABASE_URL`, `NEXTAUTH_SECRET`, and `CALENDSO_ENCRYPTION_KEY`, and give it the app's public address via `NEXT_PUBLIC_WEBAPP_URL` before creating any bookings. - `NEXTAUTH_SECRET` and `CALENDSO_ENCRYPTION_KEY` should be generated once and treated as permanent. Rotating them invalidates sessions and encrypted calendar credentials. - Every Ownkube app gets an `ownkube.app` address with automatic TLS, which Cal.com's calendar OAuth flows (Google, Outlook) require to complete correctly. - A Postgres database box starts at $4/month plus storage, and the app runs comfortably on a small reserved box given its steady traffic pattern. ## What you need before you start - The image reference `calcom/cal.com:latest`, or a pinned release tag for reproducible deploys. - A subdomain for the app on `ownkube.app`, for example `cal-yourteam.ownkube.app`. Calendar OAuth (Google, Microsoft) redirects back to this URL and needs HTTPS to complete, so pick it before you connect any calendar. - Two secrets generated ahead of time: a `NEXTAUTH_SECRET` and a `CALENDSO_ENCRYPTION_KEY`, both random strings you generate once and keep. ```bash # Generate both secrets locally before you deploy openssl rand -base64 32 # NEXTAUTH_SECRET openssl rand -base64 32 # CALENDSO_ENCRYPTION_KEY ``` ## 1. Create a managed Postgres database Cal.com's Prisma schema targets Postgres specifically; there is no supported SQLite path. Create a database box on Ownkube (from $4/month plus storage at $0.25/GB/month) and note the connection details. Cal.com expects a single connection string rather than split host and port variables, so assemble it in the [standard Postgres URL format](https://www.postgresql.org/docs/): ```bash postgresql://:@:5432/?schema=public ``` ## 2. Deploy the Cal.com image Create the app from the [official image](https://cal.com/docs/introduction/quick-start/self-hosting/installation): ```dockerfile FROM calcom/cal.com:v4.6.6 ``` Pinning a version tag rather than `latest` is worth doing here, since Cal.com ships frequently and a pinned tag keeps a database migration from running unexpectedly on a redeploy. The image listens on port `3000` by default. ## 3. Set the environment variables ```bash # Database DATABASE_URL=postgresql://:@:5432/?schema=public # Auth and encryption (generate once, keep permanently) NEXTAUTH_SECRET= CALENDSO_ENCRYPTION_KEY= # App URL NEXT_PUBLIC_WEBAPP_URL=https://cal-yourteam.ownkube.app NEXTAUTH_URL=https://cal-yourteam.ownkube.app # Email (optional but needed for booking confirmations and invites) EMAIL_FROM=notifications@example.com EMAIL_SERVER_HOST=smtp.example.com EMAIL_SERVER_PORT=587 EMAIL_SERVER_USER= EMAIL_SERVER_PASSWORD= ``` Set `NEXT_PUBLIC_WEBAPP_URL` and `NEXTAUTH_URL` to the app's public address before you connect any calendars. Calendar OAuth providers validate the redirect URL against what the app reports, and a mismatch after the fact means reconnecting every calendar integration rather than a quick edit. ## 4. Run the database migration Cal.com's Prisma schema needs its migrations applied on first boot (and after upgrades that include schema changes). The official image runs this automatically on startup in most versions, but if you deploy from a custom build, run it explicitly as a one-off command against the app: ```bash npx prisma migrate deploy ``` ## 5. Check HTTPS and connect a calendar The app's `ownkube.app` address serves HTTPS from the first deploy, with no DNS to set up. That matters here: Cal.com's Google and Microsoft calendar OAuth flows both refuse to complete a redirect back to a plain HTTP URL. ```bash curl -I https://cal-yourteam.ownkube.app ``` Log in, create your first event type, and connect a calendar from Settings to confirm the OAuth round trip works end to end. ## Sizing the deployment Cal.com is a Next.js app with a fairly steady request pattern once a team is booking through it regularly (page loads, availability checks, webhook callbacks from calendar providers), which makes a flat Spark box (from $5/month) a reasonable default, since the price stays the same however busy the week gets. A personal scheduling page with light traffic is fine on metered pricing instead, billed per minute. If self-hosted scheduling eventually needs to move onto infrastructure your company controls directly, the same Cal.com image and environment variables run unchanged in [your own AWS account](/blog/move-app-to-your-own-aws) under Ownkube's own-cloud option, with the same database migration path and the same logs and metrics attached. That is a redeploy target, not a rewrite. For a wider look at how self-hosted platforms compare on cost and control, see our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026). ## FAQ ### Does Cal.com support SQLite for a small deployment? No. Cal.com's schema is built for Postgres and there is no supported SQLite backend. A managed Postgres database box is required even for a single-user instance. ### What happens if I rotate `CALENDSO_ENCRYPTION_KEY`? Rotating it breaks decryption of previously stored calendar credentials and tokens, forcing every connected calendar to be reconnected. Generate it once before first deploy and keep it out of version control but permanently saved somewhere safe. ### Do I need to run database migrations manually? The official image typically applies Prisma migrations automatically on boot. If you build a custom image or skip that step, run `npx prisma migrate deploy` against the app once before serving traffic. ### Why does calendar sync fail after I change the app's address? Google and Microsoft OAuth validate the redirect URL against what's registered for the app. Changing `NEXTAUTH_URL` or `NEXT_PUBLIC_WEBAPP_URL` after connecting calendars means reconnecting each one so the provider accepts the new redirect. ### What does this cost to run? A Postgres database box starts at $4/month plus storage. The app itself fits well on a Spark box from $5/month, or metered pricing for lighter, more occasional use. ## Where Ownkube fits Cal.com needs Postgres, two permanent secrets, and a stable HTTPS address to work correctly with calendar providers, and Ownkube Compute gives you all three without provisioning a cloud account first. Deploy the image, attach [managed Postgres](/blog/add-managed-postgres-database), and set the auth keys once. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to self-host Directus on Ownkube > A step-by-step guide to self-hosting Directus, the open-source headless CMS, on Ownkube with a managed Postgres database, a Valkey cache, and automatic TLS. - Canonical: https://ownkube.io/blog/deploy-directus - Markdown: https://ownkube.io/blog/deploy-directus.md - Published: 2026-08-23 - Author: Abhishek Jaiswal - Category: How-To - Tags: directus, self-hosting, headless-cms, deploy, tutorial Directus turns an existing Postgres database into an admin studio with a REST and GraphQL API, and you can have it running on Ownkube in a few minutes with the official image, a managed database, and a public URL that already has TLS on it. This walks through the fastest shipped path (Ownkube's Directus recipe in the Marketplace), the exact environment variables it wires up so you know what is actually happening under the hood, and the manual path if you want to build your own image instead. **TL;DR** - Deploy Directus from Ownkube's Marketplace using the official `directus/directus` image, pinned to a known tag, no Dockerfile required. - Ownkube provisions a [managed Postgres database](/blog/add-managed-postgres-database) box (from $4/month) and wires the connection string into `DB_CONNECTION_STRING` for you. - `KEY` and `SECRET`, Directus's own app secrets, plus the admin password, are generated automatically and revealable after deploy. You only supply the admin email. - A [managed Valkey cache](/blog/add-valkey-redis-cache) is wired in too, since a shared cache is Directus's own recommended production setup. - The app gets a public `*.ownkube.app` hostname with automatic TLS as soon as its health check passes. ## What you need before you start An Ownkube account is all it takes, no cloud account of your own required. The Personal plan is $5 a month, which loads $5 of wallet credit you then spend on the database box and the app's compute. Sign in at [app.ownkube.io](https://app.ownkube.io) if you have not already. ## Step 1: Open the Directus recipe in the Marketplace Ownkube's Marketplace has a Directus recipe that provisions three resources in one action: the Directus web app, a managed Postgres database, and a managed Valkey cache. Open it at `app.ownkube.io/dashboard/marketplace/directus-postgres`. Sign in to browse and deploy. Marketplace is also one click away in the dashboard's top nav and the ⌘K palette. ## Step 2: Set the one input Directus needs The recipe asks for a single value: the email for the first Directus admin account. ```bash ADMIN_EMAIL=you@example.com ``` You do not pick a password here. Directus's admin password is generated for you and shown on the deployment page after it comes up, so there is nothing to type twice or forget. ## Step 3: Choose your Postgres box size Managed Postgres on Ownkube runs on a reserved database box starting at $4 a month, plus storage at $0.25 per GB a month, with backups available on Personal and Team. It is private to the app's region by default and can run with opt-in public access and, on Team, high availability (a primary plus up to two standby instances) when you need them. Pick a box size on the deploy form; this is what actually gets billed from your wallet for the database, separate from the app's own compute. ## Step 4: Deploy and let Ownkube wire the environment Once you confirm, Ownkube provisions the database first, then starts the Directus container from the official `directus/directus` image (currently pinned at `11.17.4`) with the connection details already wired in. The environment it sets looks like this: ```bash DB_CLIENT=pg DB_CONNECTION_STRING=postgresql://app:***@:5432/app CACHE_ENABLED=true CACHE_STORE=redis REDIS=redis://:***@:6379 ADMIN_EMAIL=you@example.com KEY= SECRET= ADMIN_PASSWORD= ``` `DB_CONNECTION_STRING` is Directus's own env for a single [Postgres URI](https://www.postgresql.org/docs/), so there is no separate host, port, and credentials to assemble by hand. `REDIS` takes the cache's connection URI directly, and `CACHE_STORE=redis` moves Directus's API cache off the container's local disk and onto the shared Valkey cache, so it survives restarts and stays consistent if you ever scale to more than one replica. `KEY` and `SECRET` are Directus's own secrets for signing tokens and encrypting data, generated once at launch and stored, not something you need to invent yourself. Ownkube waits for a readiness check against `/server/health` before it exposes the app publicly, so the URL stays private until Directus is actually able to serve requests. ## Step 5: Log in and reveal the generated password Once the deployment is healthy, Ownkube assigns it a public hostname on the shared region edge with [TLS already issued](/blog/automatic-https-custom-domain), no certificate to request or renew. Open it, reveal the generated admin password on the deployment page, and sign in with the email you set in step 2. From there [Directus's admin studio](https://directus.io/docs/) is where you build collections, each one becoming a table in your own Postgres database. ## Deploying a customized Directus image instead The recipe above is the fastest way to run Directus's official image as-is. If you need to bake in your own extensions or a custom Dockerfile on top of `directus/directus`, connect a GitHub repository instead: Ownkube builds from a Dockerfile if the repo has one, or auto-detects and builds if it does not. In that path you add a managed Postgres database to the project yourself, copy the connection string from the database's Connect tab, and set the same environment variables by hand: ```bash DB_CLIENT=pg DB_CONNECTION_STRING= KEY= SECRET= ADMIN_EMAIL=you@example.com ADMIN_PASSWORD= ``` The Marketplace recipe does this wiring for you automatically; a custom image build means doing it by hand, which is worth it only if you actually need to change what is inside the container. ## Move it to your own AWS later, with no rewrite If Directus outgrows what you want to run on Ownkube Compute, the same app, the same Postgres data, and the same image move into your own AWS account with no rewrite. Logs, metrics, and health checks travel with it, so you are not starting your observability over. If you are weighing this against a platform like Railway, our [Railway alternative comparison](/blog/railway-alternative-2026) covers how that portability plays out in practice. ## FAQ ### Is self-hosting Directus on Ownkube free? No. There is no free tier on Ownkube Compute, but you don't need a subscription either: top up from $5 and pay as you go, or pick the Personal plan at $5 a month, which loads $5 of wallet credit. The database box for this recipe starts at $4 a month. Unused wallet credit rolls over and never expires, so a quiet month is not money lost. ### Does Ownkube run the actual official Directus image? Yes. The recipe pulls `directus/directus` straight from [Docker Hub](https://docs.docker.com/) at a pinned version, not a custom fork or a repackaged build. ### Can I use MySQL instead of Postgres? Not today. Postgres is the only managed relational database engine on Ownkube. ### Do I need to manage a TLS certificate myself? No. The public hostname Ownkube assigns gets TLS automatically, issued and renewed on the shared region edge. ### Where do I find the generated admin password later? It stays revealable on the deployment's detail page in the dashboard, so you can look it up again if you lose it. ## Where Ownkube fits If you want Directus's admin studio and API without running Postgres, a cache, and a container host yourself, the Marketplace recipe gets you there with the official image, generated secrets, and a TLS-covered URL in one action. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## Deploy a web app on Ownkube in under a minute > Connect a GitHub repo or push a Dockerfile to Ownkube Compute and get a live HTTPS URL in about a minute. No cloud account, no config, no subscription to start. - Canonical: https://ownkube.io/blog/deploy-a-web-app-in-under-a-minute - Markdown: https://ownkube.io/blog/deploy-a-web-app-in-under-a-minute.md - Published: 2026-08-22 - Author: Abhishek Jaiswal - Category: How-To - Tags: deploy-app, git-push, automatic-tls, ownkube-compute, tutorial Connect a repository, pick a region, and Ownkube gives you a live HTTPS URL before the coffee finishes brewing. There is no cloud account to set up first, no YAML to write, and no server to provision by hand. This walks through the whole path on Ownkube Compute, the managed infrastructure Ownkube runs for you: from an empty dashboard to a running app with a real TLS certificate on it. **TL;DR** - Sign in to `app.ownkube.io`, connect a GitHub or GitLab repo (or point at an image), and deploy. - If your repo has a [Dockerfile](https://docs.docker.com/reference/dockerfile/), Ownkube builds from it. If it does not, Ownkube detects the language and builds automatically. - You get a live URL on an auto-allocated `*.ownkube.app` hostname with TLS already on, no certificate setup required. - No cloud account, no config file, and no subscription: top up from $5 and deploy. - Every following `git push` rebuilds and rolls out with zero downtime. ## What you need - An Ownkube account (sign in at `app.ownkube.io`). - A GitHub or GitLab repository with a web app in it, or a [container image](/blog/deploy-docker-image) if you would rather deploy from one. - Nothing from AWS, GCP, or Azure. Ownkube Compute is Ownkube-managed infrastructure, so there is no cloud account to connect for this path. ## Step 1: Sign in and start a deployment Sign in to the dashboard at `app.ownkube.io` and start a new deployment. This is the primary way to work with Ownkube: one dashboard for connecting a repo, watching a build, and reading logs after it ships. ## Step 2: Point it at your code Connect your GitHub or GitLab account and pick the repository and branch to deploy. From here Ownkube decides how to build the image: - **Repo has a [Dockerfile](https://docs.docker.com/):** Ownkube builds from it. If there is more than one Dockerfile in the repo, you pick which one. - **Repo has no Dockerfile:** Ownkube detects the language and framework and builds the image automatically. The dashboard shows this as the "Auto" build target, so you can see which path a given deployment is using. Either way, the build runs on Ownkube's side and the resulting image is pushed to a managed registry, so you never have to stand up your own container registry for this. ## Step 3: Pick a region and deploy Choose a region and start the deploy. Ownkube provisions the deployment on Compute, allocates a public hostname on the shared `*.ownkube.app` domain, and terminates [TLS](/blog/automatic-https-custom-domain) on it automatically. There is no certificate to request or renew and no DNS record to create for this step: the hostname and the certificate both exist the moment the deployment is ready. ## Step 4: Watch it go live The dashboard shows the build running, then the rollout. When it finishes, you get a URL you can open right away, already serving over HTTPS. For a small web app this whole path, from connecting the repo to a working URL, typically takes under a minute. You can also check on a deployment from a terminal with the `okctl` CLI (`brew install ownkube/tap/okctl`, then `okctl login` to authenticate in your browser): ```bash okctl login okctl deploy list okctl deploy status ``` `status` reports build sync, health, and whether the URL is ready to serve traffic, which is useful if you are scripting a deploy or wiring it into an agent. ## Step 5: Ship your next change Push a commit to the connected branch and Ownkube rebuilds the image and rolls the new version out with zero downtime: the old version keeps serving traffic until the new one is healthy. If a deploy goes wrong, the dashboard's rollback control puts the previous version back in front of traffic. If something looks off after a rollout, pull the recent logs without leaving your terminal: ```bash okctl deploy logs --range 600 ``` That fetches log entries from the last ten minutes (`--range` is in seconds), which is usually enough to see whether a bad deploy is a build problem or a runtime one. ## Moving it later, without a rewrite Ownkube Compute is the fast path: no cloud account, nothing to provision. If you later want your own data boundary or your own AWS bill for this same app, you can connect an AWS account and run the identical app there, with no rewrite. The build, the dashboard, the logs, and the metrics all carry over. That option is worth knowing about, not something you need to think about on day one. If you are actively weighing platforms, our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) covers how each handles this kind of portability. ## What to add next A web app rarely stands alone. Once this deployment is live, the two most common next additions are a database and a cache: - [How to add a managed Postgres database to your app](/blog/add-managed-postgres-database) - [How to add a Valkey (Redis) cache to your app](/blog/add-valkey-redis-cache) Both attach to the app you just deployed, and both draw from the same [wallet](/blog/prepaid-wallet-no-surprise-bill) as your Compute usage, so there is nothing extra to set up on the billing side. ## FAQ ### Do I need a Dockerfile to deploy? No. If your repo has one, Ownkube builds from it. If it does not, Ownkube detects the language and framework and builds the image automatically. Both paths land on the same managed registry and deployment flow. ### Do I need to connect a cloud account first? No. Ownkube Compute is managed infrastructure that Ownkube runs, so deploying an app does not require an AWS, GCP, or Azure account. Connecting your own AWS account is a separate, optional path for later. ### Can I use my own domain instead of the ownkube.app hostname? Custom domains are shipped for apps running on your own AWS account (Starter clusters), where you register a domain once and Ownkube manages a certificate covering it. On Ownkube Compute, today's deployments get the auto-allocated `*.ownkube.app` hostname with TLS on by default. ### What happens when I push a new commit? Ownkube rebuilds the image and rolls the new version out with zero downtime, so the previous version keeps serving requests until the new one passes its health check. You can roll back to the prior version from the dashboard if needed. ### How fast is "under a minute", really? It depends on your build (a larger dependency tree takes longer to build than a small script), but for a typical small web app the path from connecting a repo to a live, TLS-covered URL is around a minute end to end. **Reference:** For the step-by-step reference version, see the [Deploy your first app](/docs/guides/deploy-first-app) guide in the docs. ## Where Ownkube fits If you want a running, HTTPS-covered app without touching a cloud console first, this is the whole setup: connect a repo, pick a region, and deploy. The build, the certificate, and the hostname are handled for you, and every push after that just rolls forward. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## Connect your AI coding agent to Ownkube with MCP > Add Ownkube's hosted MCP server to Claude Code, Cursor, Codex, Windsurf, Zed, or Claude Desktop, sign in once, and deploy without leaving your editor. - Canonical: https://ownkube.io/blog/connect-ai-agent-mcp - Markdown: https://ownkube.io/blog/connect-ai-agent-mcp.md - Published: 2026-08-21 - Author: Abhishek Jaiswal - Category: How-To - Tags: mcp, ai-agents, claude-code, cursor, tutorial Ownkube runs a hosted MCP server at `app.ownkube.io/api/mcp`, so a coding agent that speaks the [Model Context Protocol](https://modelcontextprotocol.io/) can deploy your app, read its logs, and manage its database without you switching to a browser tab. This is a walkthrough of adding it to the agents people actually use: Claude Code, Cursor, Codex, Windsurf, Zed, and Claude Desktop. **TL;DR** - The Ownkube MCP server lives at `https://app.ownkube.io/api/mcp` and uses OAuth 2.1 with browser sign-in, so there is no API key to paste into a config file. - Each client adds the server slightly differently: a one-line CLI command for Claude Code, a JSON file for Cursor and Windsurf, a config entry through the `mcp-remote` bridge for Codex, Zed, and Claude Desktop. - Signing in once opens a browser tab where you pick an organization; the connection is scoped to that org until you change it. - The tools cover deploying apps, workers, and jobs, reading logs and metrics, managing databases, custom domains, alerts, and billing. - Two destructive steps are deliberately kept out of the agent's reach: database password resets and point-in-time recovery. Those stay in the `okctl` CLI. ## What the MCP server actually does Adding Ownkube as an MCP server gives your agent the same operations you'd otherwise do by hand in the dashboard: creating a deployment, checking whether it's healthy, tailing its logs, adding a [managed Postgres database](/blog/add-managed-postgres-database) or a [Valkey cache](/blog/add-valkey-redis-cache), setting a CPU or memory limit, linking a [custom domain](/blog/automatic-https-custom-domain), or checking your wallet balance. The agent calls these as structured tools, not a shell wrapped around the dashboard, so it gets back data it can reason over instead of screen text to guess at. It does not get everything. Resetting a database password and running point-in-time recovery are excluded on purpose. Those actions stay behind `okctl`, the command-line client, where you're the one typing the command. ## Before you connect You need an Ownkube account (sign up at `app.ownkube.io`) and a client that supports remote MCP servers over HTTP. There's nothing to install on the Ownkube side: the server is already running, and connecting is a client-side config change plus a browser sign-in. ## Connect Claude Code [Claude Code](https://docs.claude.com/en/docs/claude-code) has a built-in command for adding a remote MCP server: ```bash claude mcp add --transport http ownkube https://app.ownkube.io/api/mcp ``` Run `/mcp` inside Claude Code and choose `ownkube` to authenticate. A browser tab opens, you sign in, pick an organization, and control returns to your terminal. From there you can ask it things like "deploy this repo to Ownkube Compute and give me the live URL" or "tail the logs for my api service and tell me why it keeps restarting." ## Connect Cursor [Cursor](https://docs.cursor.com/) reads its MCP servers from a JSON config file. Add this to `~/.cursor/mcp.json` (or a project-level `.cursor/mcp.json` if you want the connection scoped to one repo): ```json { "mcpServers": { "ownkube": { "url": "https://app.ownkube.io/api/mcp" } } } ``` Open Settings, then MCP, and toggle the `ownkube` server on. Cursor opens a browser tab for the same sign-in and organization pick, then shows the server as connected in Composer. ## Connect Codex, Windsurf, Zed, and Claude Desktop These four don't speak remote HTTP MCP servers directly. Each bridges to the same endpoint with `mcp-remote`, a small proxy that handles the OAuth browser flow for stdio-based clients. It needs Node and `npx` on your machine, and the first connection opens the same browser sign-in as Claude Code and Cursor. | Client | Config file | Entry | |---|---|---| | Codex | `~/.codex/config.toml` | `[mcp_servers.ownkube]` block running `npx -y mcp-remote https://app.ownkube.io/api/mcp` | | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `"ownkube": { "serverUrl": "https://app.ownkube.io/api/mcp" }` | | Zed | `settings.json`, under `context_servers` | custom source running `npx -y mcp-remote https://app.ownkube.io/api/mcp` | | Claude Desktop | `claude_desktop_config.json` | `"ownkube": { "command": "npx", "args": ["-y", "mcp-remote", "https://app.ownkube.io/api/mcp"] }` | For example, Codex's `~/.codex/config.toml` looks like this: ```toml [mcp_servers.ownkube] command = "npx" args = ["-y", "mcp-remote", "https://app.ownkube.io/api/mcp"] ``` After saving, restart or refresh the client so it picks up the new server, then trigger a request that needs it. `mcp-remote` opens the browser tab, you sign in and pick an organization, and the tools show up from then on. ## What you can ask it to do Once connected, the tools group roughly into deployments (create, list, check status, restart, roll back), logs and metrics, databases and caches, clusters and cloud accounts, custom domains, alerts, and billing. In practice that covers requests like: - "Deploy this repo to Ownkube Compute and give me the live URL." - "Add a managed Postgres database and wire the connection string into my app." - "Set a CPU and memory limit on my web service, then show me the projected monthly cost." - "Roll back my last deploy to the previous healthy revision." - "Check my wallet balance and top it up if it's under five dollars." The agent picks a region and creates the deployment on Ownkube Compute, so there's no cloud account or cluster to provision first. ## A note on where the app can run later Everything above deploys onto Ownkube-managed compute, no AWS account required. If you later want the same app running in your own AWS account instead, that's a separate step, not a rewrite: the deployment, its logs, and its metrics move with it. Our guide on [moving the same app into your own AWS account](/blog/move-app-to-your-own-aws) covers that path in detail. ## FAQ ### Do I need an API key to connect an agent to Ownkube? No. The MCP server uses OAuth 2.1 with browser sign-in and org-scoped consent. There's no API key to generate or paste; the `x-api-key` path is for the `okctl` CLI only. ### Is it safe to let an agent operate my Ownkube account? You approve access through a consent screen scoped to one organization, and you can revoke the connection at any time. Destructive database steps, resetting a password and point-in-time recovery, are deliberately excluded from the agent's tool list. ### Which organization does the agent act on? The one you pick on the consent screen during sign-in. If you belong to more than one, you can pass an organization explicitly per request; otherwise the agent falls back to the one you consented to. ### What happens if the agent's access token expires? Calls start failing, and the client re-runs the browser sign-in to mint a fresh token the next time it needs the server. You don't need to remove and re-add the connection. ### Can I use this with an editor that isn't in this list? If the editor or agent supports remote MCP servers over Streamable HTTP, point it at `https://app.ownkube.io/api/mcp` directly. If it only supports local stdio servers, bridge it with `mcp-remote` the same way Codex, Windsurf, Zed, and Claude Desktop do. ## Where Ownkube fits If you already work through an agent all day, having it deploy, read logs, and manage a database directly is faster than narrating the same steps into a dashboard. The MCP server exposes the real platform, not a scripted demo: the tools it calls are the same ones the dashboard uses, and the actions it can't take are the ones you'd want to type yourself anyway. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to autoscale a service on CPU and memory > Turn on horizontal autoscaling for an Ownkube deployment: set min and max replicas, CPU and memory targets, and see why idle replicas barely touch your bill. - Canonical: https://ownkube.io/blog/autoscale-cpu-memory - Markdown: https://ownkube.io/blog/autoscale-cpu-memory.md - Published: 2026-08-20 - Author: Abhishek Jaiswal - Category: How-To - Tags: autoscaling, scaling, performance, tutorial, ownkube-compute A single replica is fine until traffic isn't steady: a launch post spikes requests for an hour, then it's quiet again. Rather than picking one fixed size and living with either overpaying at 3am or falling over at noon, Ownkube can add and remove replicas of a deployment automatically, based on how hard CPU and memory are actually working. This guide turns on [horizontal autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) for a web or [worker deployment](/blog/background-worker-job-queue), explains what the CPU and memory targets actually control, and covers what it does to your bill. **TL;DR** - Autoscaling is a per-deployment setting: min and max replica counts, plus a target CPU percentage and a target memory percentage. - Ownkube adds replicas when either target is exceeded and removes them as load drops, never going below your minimum or above your maximum. - New replicas roll in with zero-downtime rollouts, so scaling up (or deploying a new revision while scaled up) doesn't drop traffic. - On Ownkube Compute's metered billing, CPU and memory bill on actual per-minute usage, so a replica sitting idle between traffic spikes costs close to nothing. - Set it from the deployment's Settings tab, or the same fields from the CLI via a manifest update. ## Turn on autoscaling 1. Open the deployment (a `web` or `worker` type; jobs and databases don't autoscale) and go to its **Settings** tab. 2. Find the **Autoscaling** card and flip its switch on. 3. Set **Min replicas** and **Max replicas**. Min is your floor, the number of replicas that stay running even at zero load; max is your ceiling, how far Ownkube can scale up under sustained pressure. 4. Set **Target CPU %** and **Target Memory %**. These are the utilization levels, as a percentage of each replica's resource limit, that trigger a scale-up when crossed. 5. Save. The change applies without downtime; existing replicas keep serving traffic while the new target takes effect. ```yaml # manifest.yaml, applied with: okctl deploy update -f manifest.yaml appConfig: autoscaling: enabled: true minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 70 targetMemoryUtilizationPercentage: 80 ``` ## How the targets actually work Autoscaling here is horizontal: Ownkube changes how many replicas of your deployment are running, not the size of any single replica. Each replica still runs at the CPU and memory limits you set on the Resources tab; the target percentages are how full those limits need to get before another replica joins. - If **Target CPU %** is 70 and a replica's CPU usage sits above 70% of its limit for a sustained period, Ownkube adds a replica and spreads load across both. - The same logic applies independently to **Target Memory %**. Whichever trigger fires first drives the scale-up; either one dropping back below target (with both below target) is what allows scaling back down. - Replicas never drop below **Min replicas**, even at zero traffic, and never exceed **Max replicas**, even under sustained load. Set the max to whatever ceiling keeps you comfortable, both for capacity and for spend. A good starting point for most web apps is a target in the 60 to 80% range on both CPU and memory: low enough to add capacity before things actually feel slow, high enough that you're not scaling up on every minor blip. ## Scaling and deploys stay zero-downtime Autoscaling shares the same [rolling-update mechanics](https://kubernetes.io/docs/) as a normal deploy: a new replica has to pass its health check before it starts receiving traffic, and an old replica is only removed after its replacement is ready. That holds whether the new replica showed up because you [pushed a commit](/blog/deploy-a-web-app-in-under-a-minute) or because load crossed your CPU target, so a traffic spike and a deploy landing at the same time don't fight each other or drop requests. ## What autoscaling does to your bill On Ownkube Compute, [metered billing](/pricing) measures both CPU and memory on actual per-minute usage, not on the size you provisioned. That changes the economics of scaling up compared to a platform that bills per fixed instance-hour regardless of load: - A replica that autoscaling added during a traffic spike and then removed an hour later only metered for that hour, not for the rest of the month. - A replica sitting at your minimum during quiet hours draws almost nothing on CPU, since the meter reflects what's actually used, not the limit it's allowed to reach. - If a workload runs hot around the clock instead of bursting, a flat-rate reserved box can end up cheaper than metering continuously; you can put a steady worker on a reserved box and leave autoscaling for the bursty web app in the same project. See our breakdown of [metered versus reserved pricing](/blog/metered-vs-reserved-pricing) for how to decide per deployment. The practical effect is that autoscaling on Ownkube is closer to "pay for the shape your traffic actually took" than "pay for the ceiling you configured," which is the opposite of how a fixed-instance-hour platform prices the same setup. ## FAQ ### What's the difference between autoscaling and just setting a bigger resource limit? A resource limit caps how much CPU and memory a single replica can use. Autoscaling changes how many replicas exist. A CPU-bound app that's maxing out one replica benefits more from another replica (horizontal) than a bigger limit on the same one (vertical), especially since most web frameworks don't use extra cores within a single process well. ### Does scaling up cause downtime? No. New replicas join through the same zero-downtime rollout path as a normal deploy: they pass a health check before receiving traffic, and old replicas are removed only after replacements are ready. ### Can I autoscale a database or a scheduled job? No. Autoscaling applies to web and worker deployments. Databases and caches are always reserved, fixed-size boxes, and a scheduled job runs to completion rather than serving ongoing traffic, so neither has a replica count to scale. ### Will autoscaling make my bill unpredictable? It can move month to month if your traffic does, but on metered billing each replica only draws the wallet for the minutes it actually runs and the resources it actually uses, capped by your max replicas. If predictability matters more than squeezing idle time, put the deployment on a reserved box instead and skip autoscaling. ### What's a reasonable max replicas to start with? Whatever ceiling protects your wallet and your dependencies (like a database's connection limit) if load spikes hard. Start conservative, watch how often you actually approach it on the deployment's metrics, and raise it if you see autoscaling pinned at the max during real traffic. **Reference:** For the full reference, see [Autoscaling](/docs/features/autoscaling) in the docs. ## Where Ownkube fits If your traffic isn't flat, autoscaling means you're not choosing between overpaying for headroom you rarely use and getting paged when you didn't provision enough. Set a floor, a ceiling, and CPU and memory targets, and Ownkube adds and removes replicas through zero-downtime rollouts while metered billing keeps the cost tied to what actually ran. For the bigger picture on right-sizing spend across a whole project, see our guide to [Kubernetes cost optimization for startups](/blog/kubernetes-cost-optimization-startups). [Deploy your first app](https://app.ownkube.io/login). --- ## How to add a background worker and job queue > Deploy a queue-backed worker on Ownkube: a Valkey cache from $2/mo, a BullMQ producer and consumer, worker autoscaling, and workers versus scheduled jobs. - Canonical: https://ownkube.io/blog/background-worker-job-queue - Markdown: https://ownkube.io/blog/background-worker-job-queue.md - Published: 2026-08-20 - Author: Abhishek Jaiswal - Category: How-To - Tags: background-worker, queue, valkey, jobs, tutorial Move slow work off your request path and onto a worker, and the queue between the two is usually the part that goes undocumented. This walks through provisioning a [Valkey](https://valkey.io/) cache as the queue backend, deploying a worker to consume it, scaling that worker, and where a scheduled job fits instead. **TL;DR** - A **worker** deployment is a long-running background process with no public endpoint, the right shape for a queue consumer. - A **[Valkey cache](/blog/add-valkey-redis-cache)** ([Redis-compatible](https://redis.io/docs/)) is the queue backend. It is always reserved, not metered, starting at **$2/mo** for a Cache 0.25 box. - A producer (your web app) pushes jobs onto the queue; a consumer (the worker) pulls them off. BullMQ is a common choice on Node, and the same pattern holds for Celery or RQ on Python. - Turn on horizontal autoscaling on the worker to add replicas under queue pressure and scale back down when it drains. - A worker is always running and drains continuously. A [scheduled job](/blog/run-scheduled-cron-jobs) runs to completion on a [cron schedule](https://crontab.guru/) and exits. Pick based on whether the work is continuous or periodic. ## Step 1: create the Valkey cache From your [dashboard](https://app.ownkube.io/dashboard), add a new deployment and choose **Cache** as the resource type. Pick the smallest box to start: | Box | vCPU | Memory | Price | |---|---|---|---| | Cache 0.25 | 0.1 vCPU (shared) | 0.25 GiB | $2/mo | | Cache 0.5 | 0.1 vCPU (shared) | 0.5 GiB | $3/mo | | Cache 1 | 0.25 vCPU (shared) | 1 GiB | $5/mo | Caches are always reserved, so the price is flat for as long as the cache exists, busy or idle. There is no storage charge because a cache is in-memory and ephemeral: a restart clears it, which is fine for a job queue where in-flight jobs are re-enqueued or lost gracefully, but worth knowing before you point anything at it that needs durability. ## Step 2: get the connection string Open the cache's **Connection details** card. It shows the host, port, password, and a ready-to-use `redis://` connection string, plus a `redis-cli` one-liner for a quick manual check. The cache is private by default, reachable only from your own deployments on the private network; there is no public endpoint unless you opt in. Copy the connection string and set it as a **Secret** environment variable, `REDIS_URL`, on both the deployment that will enqueue jobs and the worker that will process them. See [how to manage secrets and environment variables](/blog/manage-secrets-env-vars) if you have not set one before. ## Step 3: write a producer Anywhere in your existing web app that currently does slow work inline, for example sending an email or generating a report, push a job onto the queue instead of doing the work in the request: ```ts // queue.ts import IORedis from "ioredis"; import { Queue } from "bullmq"; const connection = new IORedis(process.env.REDIS_URL!, { maxRetriesPerRequest: null, }); export const emailQueue = new Queue("emails", { connection }); ``` ```ts // in your request handler await emailQueue.add("welcome", { userId: user.id, email: user.email }); res.status(202).json({ queued: true }); ``` The request returns immediately. The email itself gets sent by the consumer. ## Step 4: deploy the worker Create a new deployment and choose **Worker** as the resource type. A worker has no public hostname; it just runs. Point it at an image that starts your consumer process, and set the same `REDIS_URL` secret from Step 2: ```ts // worker.ts import IORedis from "ioredis"; import { Worker } from "bullmq"; const connection = new IORedis(process.env.REDIS_URL!, { maxRetriesPerRequest: null, }); new Worker( "emails", async (job) => { await sendWelcomeEmail(job.data.email); }, { connection, concurrency: 5 } ); ``` Deploy it, and check the worker's **Logs** tab to confirm it connected and is pulling jobs. If you are on Python, the same shape works with Celery (Valkey as the broker) or RQ; only the client library changes, since Valkey speaks the Redis protocol. ## Step 5: scale the worker A single worker replica processing five jobs at a time is fine until the queue backs up faster than it drains. Turn on **autoscaling** on the worker deployment from its **Settings** tab, the same feature web deployments use: - **Min replicas**: keep at least one worker running, so the queue is never unattended. - **Max replicas**: a ceiling, up to 100. - **Target CPU utilization**: scale up once average CPU crosses your target (queue consumers are often CPU-bound during processing and idle between jobs, so this responds well to a burst of work). Autoscaling reacts within seconds of a sustained change and removes replicas again once usage drops, so a burst of signups that floods the email queue gets more workers automatically, and you are not paying for five replicas sitting idle overnight. ## Worker versus scheduled job Both run on Ownkube, and it is easy to reach for the wrong one. A **worker** is a long-running process. It starts once and keeps running, continuously pulling from the queue as jobs arrive. Use it for anything continuous: queue consumers, event processors, a WebSocket relay. A **scheduled job** runs a container to completion and exits, on a cron schedule you set when you create it. It has its own **Runs** tab with recent run history, a **Run now** button to fire an off-schedule run, and per-run controls for retries, timeouts, and overlap handling. Use it for periodic work that does not need a process sitting around between runs: a nightly export, a weekly cleanup, a report that regenerates every hour. If your workload is "do this continuously as items arrive," it is a worker. If it is "do this once, on a schedule," it is a job. A queue consumer is almost always a worker, since jobs arrive at unpredictable times and something needs to be listening. ## FAQ ### Does the worker scale to zero when the queue is empty? No. Scale to zero is for web apps, since an incoming request is what wakes them, and a worker has none. The floor is one replica once autoscaling is on. If you want a worker to stop entirely between bursts, pause it to 0 replicas manually; autoscaling will not do that for you. ### Do I have to use BullMQ? No. Valkey is Redis-compatible, so any Redis-protocol queue library works: BullMQ or a raw Redis list on Node, Celery or RQ with a Redis broker on Python, Sidekiq on Ruby. The cache does not care which client connects to it. ### What happens to queued jobs if the cache restarts? A Valkey cache is in-memory and ephemeral, so a restart clears it. Most queue libraries handle in-flight job loss by design (BullMQ jobs that were claimed but not acknowledged become stalled and get retried), but anything already fully processed and acknowledged is gone. Do not use the cache as a durable store for jobs you cannot afford to lose; use it as a queue. ### Can I reach the cache from outside Ownkube? Not by default. A cache is private, reachable only from your own deployments, unless you turn on public access, which allocates a TLS-encrypted `rediss://` endpoint. ### How is a cache priced compared to a metered worker? Differently, on purpose. The worker is metered: CPU and memory bill on actual per-minute usage, so it costs little while idle between bursts. The cache is reserved: a flat monthly price for the box you picked, whether it is busy or idle, because it holds memory continuously. Both draw the same [prepaid wallet](/pricing). ## Where Ownkube fits A queue and a worker are a small amount of infrastructure, but they are exactly the kind of thing that is annoying to hand-wire: provisioning a cache, getting the connection string into two different deployments, and getting autoscaling right so a traffic spike does not silently pile up in the queue. On Ownkube it is three deployments (web, cache, worker) and one setting to flip. The same setup runs the same way if you later move it into your own AWS account; see our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026) for how queue and worker pricing stacks up elsewhere. [Deploy your first app](https://app.ownkube.io/login). --- ## How to set up automatic HTTPS on a custom domain > Connect a domain you own to an Ownkube app: register it once, get a managed certificate for the root and every subdomain, then link a host. - Canonical: https://ownkube.io/blog/automatic-https-custom-domain - Markdown: https://ownkube.io/blog/automatic-https-custom-domain.md - Published: 2026-08-19 - Author: Abhishek Jaiswal - Category: How-To - Tags: custom-domain, tls, https, dns, tutorial [Deploy a web app](/blog/deploy-a-web-app-in-under-a-minute) on Ownkube Compute and it gets a working `*.ownkube.app` address with [HTTPS](https://letsencrypt.org/docs/) the moment it goes live, automatically, with nothing to request and nothing to renew. That's the default for every app: no cloud account, no config, no certificate to manage. Some apps also need a domain the business already owns, `yourcompany.com` instead of `yourcompany.ownkube.app`. Ownkube manages that certificate too, today on a Starter (K3s) cluster in your own AWS account: one setup per domain, then any subdomain or the root domain itself can point at an app with a single DNS record. This guide covers what you get on Ownkube Compute by default, where custom domains apply today, and how to register a domain and link it to a public web app. **TL;DR** - Every Ownkube Compute app already has automatic HTTPS on its own `*.ownkube.app` hostname, no setup required. That's the default and it needs nothing from you. - Serving an app on a domain you own is available today on a Starter (K3s) cluster in your own AWS account. Custom domains on Ownkube Compute are on the roadmap, not shipped yet. - On Starter, register the parent domain once in the cluster's Networking settings. Ownkube issues a single certificate that covers the root domain and every subdomain under it, and shows you a one-time delegation record to add. - After that record verifies, link any host, a subdomain like `app.yourcompany.com` or the root `yourcompany.com` itself, to a public web app from that app's own settings. - Each linked host needs exactly one DNS record of its own: a CNAME for a subdomain, an A record for the root. Ownkube never asks for your whole domain's nameservers. ## What you get by default on Ownkube Compute If you're deploying on Ownkube Compute, the no-cloud managed compute most personal projects and small teams start on, automatic HTTPS is already there. Every web deployment gets a public `*.ownkube.app` hostname with a valid certificate from the moment it's live, no action needed. For a lot of apps, that's the whole story: a working HTTPS address, done. ## Before you start (custom domains) Custom domains, putting your own `yourcompany.com` in front of an app, are a Starter (K3s) cluster feature today, on public web apps only, and need a recent platform version. They are not available on Ownkube Compute yet (it's on the roadmap). If you want a domain you own on the app today, run it on a Starter cluster in your own AWS account instead. The same app definition works either way, no rewrite, so this isn't a decision you're locked into. ## Register the domain on your cluster 1. Open your cluster's [dashboard](/docs) page and go to the **Networking** pane. 2. In **Custom domains**, type the parent domain you own, for example `yourcompany.com`, and click **Add**. 3. Ownkube shows a one-time **certificate verification** record: a CNAME with a specific name and target. Add it at your [DNS provider](https://developers.cloudflare.com/dns/) (wherever `yourcompany.com` is currently managed). 4. Back in the dashboard, click **Verify DNS** on the domain once the record has propagated. Status moves from *pending* to *verifying* to *active* as Ownkube confirms the delegation and issues the certificate. This one record is what lets Ownkube prove you own the domain and keep the certificate renewed automatically. It does not touch your existing nameservers or any other records already on the domain, so anything else you're running on `yourcompany.com` is untouched. Once the parent domain is active, its certificate covers the root domain and every subdomain beneath it. You will not need to repeat this step for each app. ## Link a host to your app With the parent domain active, go to the public web app you want to serve on it: 1. Open the app's **Settings** tab and find the **Custom domains** section. 2. Enter the host you want: a subdomain like `app.yourcompany.com`, or the root domain `yourcompany.com` itself if you want this app to be the site people land on. 3. Click **Link**. Ownkube shows a **traffic routing** record to add: a CNAME for a subdomain, an A record if you linked the root. 4. Add that record at your DNS provider. The host comes online automatically once it resolves, no second verify click needed. ```bash # From the CLI: link a host to a deployment okctl domains add app.yourcompany.com # Check status and see the DNS records to add okctl domains list # Re-check DNS after adding a record okctl domains verify ``` You can link more than one host to the same domain to different apps, an `app.yourcompany.com` for your product and the root `yourcompany.com` for a marketing site, for instance, and each only needs its own single routing record. ## Verify it's live Once a linked host shows **active**, hit it in a browser. You should see a valid certificate for your domain (not `*.ownkube.app`) and your app's response. If it's stuck on *pending*, double-check the exact record name and target Ownkube showed you against what's actually saved at your DNS provider: a typo in the CNAME target is the most common reason a host doesn't come online. ## Removing a domain or host Unlinking a host from an app (or removing the parent domain from the cluster entirely) stops routing for it immediately. Removing a parent domain that still has linked apps takes those apps offline over HTTPS until you move them to another domain, so unlink hosts first if you're migrating away from a domain rather than retiring it. ## FAQ ### Does this work with Ownkube Compute, or only my own AWS account? Custom domains ship today on Starter (K3s) clusters in your own AWS account. Ownkube Compute apps have automatic TLS on their `*.ownkube.app` address, but attaching your own domain to a Compute app is not available yet. ### Do I need a separate certificate for each subdomain? No. One certificate per parent domain covers the root and every subdomain under it. You only go through the one-time delegation record once per domain, not once per app. ### Can I point my domain's root (the apex) at an app instead of a subdomain? Yes. Link the bare domain (`yourcompany.com`) the same way you'd link a subdomain; Ownkube gives you an A record to add instead of a CNAME, since most DNS providers don't allow a CNAME at the apex. ### Does Ownkube ever ask me to change my domain's nameservers? No. Every step adds exactly one record, either at the domain level (the certificate delegation) or at the host level (traffic routing). Ownkube never asks you to delegate the whole zone. ### What if the certificate or the linked host gets stuck on pending? Check that the DNS record you added matches the name and target shown in the dashboard exactly, then click Verify DNS (for the parent) or wait for the automatic recheck (for a linked host). Propagation can take a few minutes depending on your provider's TTL. ## Where Ownkube fits If you want your own domain on an app with a certificate you never have to renew by hand, Ownkube handles the whole chain: one delegation record per domain, one routing record per host, and automatic status tracking in between. It's a Starter (own AWS) capability today, and the same app moves there from Ownkube Compute with no rewrite if that's where you started. For a closer look at how DNS and TLS setup compares to other platforms, see our [self-hosted PaaS comparison](/blog/self-hosted-paas-comparison-2026) and [Railway vs. Render vs. Northflank](/blog/railway-vs-render-vs-northflank-2026). See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## How to add a managed Postgres database to your app > Create a private managed Postgres database on Ownkube, copy its connection string, and wire it into your app. Database boxes start at $4 a month plus storage. - Canonical: https://ownkube.io/blog/add-managed-postgres-database - Markdown: https://ownkube.io/blog/add-managed-postgres-database.md - Published: 2026-08-18 - Author: Abhishek Jaiswal - Category: How-To - Tags: postgres, managed-database, database, ownkube-compute, tutorial A managed [Postgres](https://www.postgresql.org/docs/) database on Ownkube is a few clicks from the same dashboard you deployed your app in, and it comes with a connection string ready to drop into your app's environment variables. No separate database host to provision, no RDS console, no backup job to write yourself. This walks through creating the database, getting its connection details, and wiring it into a running app, plus what it costs and where today's managed-compute Postgres is still growing. **TL;DR** - Create a `database` deployment from the same dashboard as your apps and pick Postgres. - Choose a database box by vCPU and memory; the box bills a flat monthly price whether the database is busy or idle. - Copy the connection string from the database's connection card and set it as an [environment variable](https://12factor.net/config) on your app. - Database boxes start at $4 a month, and storage bills at $0.25 per GB per month. On Personal and Team, that storage price includes backups once you turn them on. - Managed-compute Postgres is private (in-region) and single-instance by default. Opt-in public access, backups with point-in-time recovery (Personal and Team), and high availability with automatic failover (Team) are available per database when you need them. ## What you need - An app already deployed on Ownkube (see [how to deploy a web app in under a minute](/blog/deploy-a-web-app-in-under-a-minute) if you have not done this yet). - An Ownkube account signed in at `app.ownkube.io`. - Wallet balance to cover the database box's monthly price, since databases are always reserved, never metered. ## Step 1: Create the database deployment From the dashboard, start a new deployment and choose the database type. Ownkube's managed database engine is Postgres. Pick a name, a region (put it in the same region as the app that will use it, to keep latency low), and continue to sizing. ## Step 2: Pick a database box A database is always reserved: it holds memory and runs continuously, so it is never metered like a web app. Instead it picks a fixed-size database box that bills a flat monthly price for as long as the database exists: | Box | vCPU | Memory | Price | |---|---|---|---| | DB 0.5 | 0.25 vCPU (shared) | 0.5 GiB | $4 / mo | | DB 1 | 0.5 vCPU (shared) | 1 GiB | $8 / mo | | DB 2 | 0.5 vCPU (shared) | 2 GiB | $12 / mo | | DB 4 | 1 vCPU (dedicated) | 4 GiB | $28 / mo | | DB 8 | 2 vCPU (dedicated) | 8 GiB | $54 / mo | | DB 16 | 4 vCPU (dedicated) | 16 GiB | $104 / mo | For a small app, DB 0.5 or DB 1 is plenty to start. You can move to a bigger box later if the app outgrows it. On top of the box price, storage bills separately at $0.25 per GB per month starting from the first GB. On Personal and Team that storage price covers backups once you turn them on, so there is no separate backup line to budget for. While the database sleeps, only the storage bills. ## Step 3: Get the connection string Once the database finishes provisioning, its connection card in the dashboard shows the host, port, database name, username, and password, along with a ready-to-copy [connection string](https://www.postgresql.org/docs/current/libpq-connect.html). The database is private by default: it is reachable from your other deployments in the same region, not from the public internet, which is the right default for a database holding real data. ## Step 4: Wire it into your app Set the connection string as an [environment variable](/blog/manage-secrets-env-vars) on the app that needs it. From your app's environment settings, add something like: ```bash DATABASE_URL=postgres://:@:5432/ ``` Then read it the way your framework already expects. For a typical Node app: ```ts import { Pool } from "pg"; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); const { rows } = await pool.query("select now()"); ``` Redeploy the app so the new environment variable takes effect. If you are scripting this instead of using the dashboard, the `okctl` [CLI](/cli) can read back your environment and deployment state: ```bash okctl login okctl environments get okctl deploy status ``` ## Step 5: Confirm the app can reach it Open the app's logs after the redeploy and look for a successful connection instead of a timeout or an authentication error: ```bash okctl deploy logs --range 300 ``` If the app can query the database, you are done. If it cannot, double-check that the app and the database are in the same region and that the environment variable was saved before the redeploy. ## What it costs A small app on a DB 0.5 box with a few gigabytes of data runs about $4 a month for the box plus a small storage charge, for example $5.25 a month at 5 GB of storage (5 x $0.25 = $1.25). Both draw from the same [prepaid wallet](/pricing) as the rest of your Compute usage, so there is nothing separate to bill or reconcile. If you want a cache in front of this database next, see [how to add a Valkey cache to your app](/blog/add-valkey-redis-cache). ## FAQ ### Is this Postgres or something Postgres-compatible? It is Postgres. Managed databases on Ownkube run the `postgres` engine, not a compatibility layer. ### Can I make the database publicly reachable? Yes. A database is private by default, and you can turn on public access per database from its **Database** tab. Ownkube allocates a stable public hostname with its own TLS certificate and TLS-passthrough routing; connect with TLS verification and direct TLS negotiation. Keep it off unless you need it, and prefer the private connection for production traffic. ### Does it support high availability or automatic failover? Yes, on the Team plan. Turn on high availability to run a primary plus up to two standby instances (three total) with automatic failover and streaming replication. Each standby bills as one database-box price, the same as the primary. ### Are backups included? On Personal and Team, yes. Turn on backups for a database from its settings and they are covered by the $0.25 per GB per month storage price, with no separate backup charge. You get 7 days of point-in-time recovery, so you can restore to an exact timestamp in that window. ### What happens if I need more capacity later? Move the database to a bigger box from the sizing table above. The database keeps its data; only the box size and its flat monthly price change. **Reference:** For the full reference, see [Databases](/docs/features/databases) in the docs. ## Where Ownkube fits A managed Postgres database on Ownkube is meant to be boring in the good way: pick a box, get a connection string, wire it into your app, and it keeps running at a flat monthly price. It is private and single-instance by default, with opt-in public access, backups, and high availability there when you need them, and for a personal project or a small team's production app the default shape is usually exactly right. [Deploy your first app](https://app.ownkube.io/login). --- ## How to add a Valkey (Redis) cache to your app > Create a managed Valkey cache on Ownkube, connect over the private or opt-in public rediss:// endpoint, and wire it into your app. From $2 a month. - Canonical: https://ownkube.io/blog/add-valkey-redis-cache - Markdown: https://ownkube.io/blog/add-valkey-redis-cache.md - Published: 2026-08-18 - Author: Abhishek Jaiswal - Category: How-To - Tags: valkey, redis, cache, ownkube-compute, tutorial A cache in front of your database or your slow endpoints is usually a small win with an outsized effect, and on Ownkube it is one more deployment next to the app and database you already have. Ownkube's managed cache runs [Valkey](https://valkey.io/), the [Redis-compatible](https://redis.io/docs/) in-memory store, with a connection string ready in a couple of minutes. This covers creating the cache, connecting to it privately from your app, opting into a public endpoint if you need one, and what it costs. **TL;DR** - Create a `cache` deployment from the dashboard; Ownkube's managed cache engine is Valkey, Redis-compatible. - Pick a cache box by memory; it bills a flat monthly price, in-memory and ephemeral, with no storage charge. - The cache is private by default. Connect your app to it over the in-region `redis://` endpoint. - If you need to reach it from outside Ownkube, turn on the opt-in public endpoint for a TLS-encrypted `rediss://` connection string. - Cache boxes start at $2 a month, always cheaper than a database box of the same memory since there is no storage or backup line. ## What you need - An app already deployed on Ownkube. See [how to deploy a web app in under a minute](/blog/deploy-a-web-app-in-under-a-minute) if you have not done this yet. - An Ownkube account signed in at `app.ownkube.io`. - Wallet balance to cover the cache box's monthly price, since caches are always reserved, never metered. ## Step 1: Create the cache deployment From the dashboard, start a new deployment and choose the cache type. The engine is [Valkey](https://valkey.io/docs/), a Redis-compatible in-memory store. Name it, put it in the same region as the app that will use it, and continue to sizing. ## Step 2: Pick a cache box A cache holds its whole dataset in memory and runs continuously, so like a database it is always reserved rather than metered. Every cache box is shared CPU, since a cache spends almost nothing on CPU and is bound by memory instead: | Box | vCPU | Memory | Price | |---|---|---|---| | Cache 0.25 | 0.1 vCPU (shared) | 0.25 GiB | $2 / mo | | Cache 0.5 | 0.1 vCPU (shared) | 0.5 GiB | $3 / mo | | Cache 1 | 0.25 vCPU (shared) | 1 GiB | $5 / mo | | Cache 2 | 0.25 vCPU (shared) | 2 GiB | $9 / mo | | Cache 4 | 0.5 vCPU (shared) | 4 GiB | $16 / mo | A cache box is ephemeral: it keeps data in memory only and carries no storage charge, which is why it always costs less than a database box with the same amount of memory. A node restart is a cold cache, so treat it as a cache, not a source of truth. ## Step 3: Configure it On the cache's settings, set the Valkey version, a max-memory limit, and an eviction policy (for example, evict the least-recently-used keys once memory fills up). These are the same knobs you would expect from any Redis-compatible deployment. ## Step 4: Connect from your app The cache's connection card shows the host, port, password, and a ready-to-copy connection string. It is private by default, reachable from your other deployments in the same region over `redis://`, not from the public internet. Set it as an [environment variable](/blog/manage-secrets-env-vars) on your app: ```bash REDIS_URL=redis://default:@:6379 ``` Then use it with whichever Redis-compatible client your stack already uses. For a Node app with `ioredis`: ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL); await redis.set("session:123", JSON.stringify({ userId: 1 }), "EX", 3600); const cached = await redis.get("session:123"); ``` Redeploy the app so the environment variable takes effect, then check its logs to confirm the connection: ```bash okctl login okctl deploy logs --range 300 ``` ## Step 5: Turn on public access if you need it Most apps only ever need the private endpoint, since the app and the cache sit in the same region. If you need to reach the cache from somewhere outside Ownkube, for example a local dev environment or a worker running elsewhere, turn on public access from the cache's settings. That allocates a stable public hostname and a [TLS-encrypted](/blog/automatic-https-custom-domain) `rediss://` connection string: ```bash REDIS_URL=rediss://default:@:6379 ``` Use `rediss://`, not `redis://`, for the public endpoint: the extra `s` is what tells your client to negotiate TLS. The public endpoint finds your cache by the hostname your client sends in the TLS handshake (SNI). Client libraries send it from the URL on their own, but `valkey-cli` and `redis-cli` need it passed explicitly: ```bash valkey-cli -u "rediss://default:@:6379" --sni ``` See [Connecting over the public endpoint](/docs/features/cache#connecting-over-the-public-endpoint) for GUI tools. ## What it costs A small Cache 0.25 box runs $2 a month with no storage line to add on top, since a cache is ephemeral by definition. That draws from the same [prepaid wallet](/pricing) as your app and database, so it is one more line in the same balance, not a separate bill. If you have not added a database yet, see [how to add a managed Postgres database to your app](/blog/add-managed-postgres-database). ## FAQ ### Is this actually Redis? It runs Valkey, the open-source, Redis-compatible fork. Your existing Redis client libraries and commands work against it the same way; Ownkube calls it a Valkey cache rather than Redis in its own copy. ### Does the cache persist data across restarts? No. A cache is in-memory and ephemeral by design, so a restart clears it. Use it for sessions, rate limits, and hot-path lookups you can regenerate, not as a system of record. ### Can I reset the cache password? Not today. The cache password is fixed when the cache is created, unlike a database password, which can be reset. If you need a new password, create a new cache. ### Is the public endpoint safe to use? The public endpoint is TLS-encrypted (`rediss://`) and off by default; you turn it on per cache when you actually need external access. For anything that does not need to leave Ownkube's network, the private `redis://` endpoint is the better default. ### How is pricing different from a database box? A cache box of the same memory size always costs less than a database box, because a cache has no dedicated-CPU tier, no durable volume, and no backup line. That is also why a cache is the wrong place to store data you cannot afford to lose. ## Where Ownkube fits Adding a cache should not be a project of its own: pick a box by memory, connect over the private endpoint your app already reaches, and turn on the public `rediss://` endpoint only when you actually need it from outside. It is one more reserved deployment on the same wallet as everything else you run. [Deploy your first app](https://app.ownkube.io/login). --- ## Kubernetes cost optimization for startups > The 2026 playbook to cut Kubernetes bills: right-sizing, spot, idle sleep, quotas, image pulls, NAT routing, and the change that compounds. - Canonical: https://ownkube.io/blog/kubernetes-cost-optimization-startups - Markdown: https://ownkube.io/blog/kubernetes-cost-optimization-startups.md - Published: 2026-05-18 - Author: Abhishek Jaiswal - Category: Engineering - Tags: kubernetes-cost, cloud-cost, aws, eks, k3s, startup-infrastructure The first surprise about a Kubernetes bill at a startup isn't how high it is. It's how predictable the leaks are. Across the small-team AWS and GCP bills we audit, the same seven patterns drive 70 to 90% of the waste, and they're all fixable in a single quarter. This post is the consolidated playbook. We'll cover the seven patterns, the realistic savings on each, and the one structural change that compounds them: putting the cost optimization itself on autopilot. **Skim answer:** - **The seven highest-leverage patterns:** right-sizing, spot capacity, idle environment sleep, namespace quotas, image-pull traffic reduction, NAT-routing audits, and storage class right-sizing. - **Combined impact:** typically cuts a small-team Kubernetes bill by 40 to 65%. - **Timeline:** all seven are fixable in a single quarter. ## Why startup K8s bills are usually 2x what they should be The math behind the typical waste: - Most teams set resource requests at 2x to 4x what the workload actually uses, "to be safe". Cluster autoscaler then provisions nodes for the inflated request, not the real usage. - Idle preview environments and staging clusters run 24/7 even though they're used 30 hours a week. - Container images get pulled from public registries through NAT gateways, racking up data-processing fees nobody sees. - EBS volumes are provisioned at default gp2 sizes that exceed real I/O needs by an order of magnitude. None of these are dramatic failures. They're small consistent bleeds. Add up enough of them and the cluster bill is double what it should be. ## Pattern 1: Right-size every workload The single largest lever. Most workloads we see have resource requests set during initial deployment, never revisited. A typical web pod might have a 1 vCPU / 1 GB RAM request and a 95th-percentile usage of 0.18 vCPU / 240 MB RAM. The other 80% of the request is reservation paid for and unused. **The fix**: use [Vertical Pod Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler) in recommendation mode for one to two weeks, then apply the recommendations. For long-tail workloads with bursty traffic, use VPA in `Auto` mode with sensible min/max bounds. **Realistic savings**: 25 to 45% of cluster compute. The single biggest line-item improvement on every audit. ## Pattern 2: Spot capacity for the workloads that tolerate it Stateless web pods, queue consumers, build runners, batch jobs, and preview environments are excellent candidates for spot. Database primaries, control plane nodes, and single-replica stateful services are not. We covered the full pattern in [AWS spot instances in production](/blog/aws-spot-instances-production-guide). **The fix**: mixed instance pools with on-demand base + spot for the rest. Karpenter or [Cluster Autoscaler with mixed ASGs](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-purchase-options.html) handles the orchestration. Set `PodDisruptionBudgets` and 30 to 120 second graceful shutdown windows. **Realistic savings**: 50 to 70% off the spot-eligible portion of the compute bill. For a typical small-team workload, that translates to roughly 30 to 50% off the total cluster compute. ## Pattern 3: Sleep idle environments Staging, preview, and developer sandbox environments sit idle the majority of the week. A preview environment created on Monday for a PR that merges Friday runs for ~120 hours while being used for maybe 4 hours. The other 116 hours are pure waste. **The fix**: implement an idle-detection loop that scales preview deployments to zero after N hours of no traffic, then scales back up on the next request. Tools like KEDA, scaler-controller, or platform-layer features handle this. **Realistic savings**: 50 to 75% of preview environment cost. For teams with 5 to 20 active previews at any time, this can be the second-biggest single-pattern saving. ## Pattern 4: Namespace quotas and request governance Without `ResourceQuota` on each namespace, any service team can ship a deployment that requests more than it needs. Quotas force the conversation: "you want 8 vCPU for this worker, defend it." **The fix**: a `ResourceQuota` per namespace pinned to a realistic budget. A `LimitRange` to set sensible default requests for pods without explicit requests. A monthly review where the quotas are revisited. **Realistic savings**: 10 to 20% indirect via culture change. The bigger win is preventing future drift. ## Pattern 5: Pull images from a private registry Most teams pull container images from Docker Hub, GitHub Container Registry, or Quay through a NAT gateway. Each pull is a few hundred MB. At a busy CI fleet plus rolling production deploys, that's tens of GB per day routed through NAT at $0.045 per GB. **The fix**: mirror your base images to a private [Amazon ECR](https://aws.amazon.com/ecr/) registry (or GCP Artifact Registry), enable the ECR VPC interface endpoint, and configure your image pull policy to use the mirror. Image pulls now stay on the AWS backbone at near-zero per-GB cost. **Realistic savings**: $50 to $400 per month for a typical small team, more for high-deploy-rate orgs. ## Pattern 6: Audit NAT routing NAT gateway data-processing fees are the most under-noticed cost on AWS bills. We covered the full pattern in our [NAT gateway cost guide](/blog/aws-nat-gateway-cost-fix). The short version: - Enable S3 and DynamoDB gateway endpoints (free) on every VPC. - Add interface endpoints for high-volume AWS services (Secrets Manager, STS, CloudWatch Logs). - Dual-stack your VPC and route IPv6 traffic through a free egress-only IGW. - Reduce NAT topology from 3 AZs to 2 if availability tolerates. **Realistic savings**: $200 to $1,500 per month depending on cluster traffic. ## Pattern 7: Storage class right-sizing EBS gp3 has replaced gp2 as the default sensible choice for most cluster volumes. It's cheaper, faster, and you pay only for the IOPS you provision (rather than the IOPS that scale with volume size on gp2). **The fix**: migrate from gp2 to gp3 across your cluster. Right-size volume capacity (most workloads provision 100 GB when they use 12 GB). For workloads with very low I/O, consider [sc1 or st1](https://aws.amazon.com/ebs/cold-hdd/) for cold data. **Realistic savings**: 20 to 35% on EBS line items. ## The structural change that compounds them all Each of the seven patterns above is a one-time engineering project. Implementing them all takes 2 to 4 weeks of focused work for a small team. The harder question is: what stops the drift from coming back six months later? The honest answer is "nothing, unless someone owns it." Without an explicit owner, request inflation creeps back in, new services get deployed without quotas, new environments forget the sleep schedule, new container pulls go through NAT, and the cluster bill returns to its old shape within two quarters. The structural fix is to put the platform itself on top of these decisions instead of leaving them to tribal memory. On [Ownkube](https://ownkube.io), running inside your own cloud account, the platform ships with the primitives that keep these patterns from drifting: - **Managed Postgres**, so database sizing and backups aren't a manual task per environment. - **Horizontal autoscaling** on every workload, so requests track real traffic instead of a number set at first deploy and never revisited. - **Built-in logs and metrics**, so a right-sizing pass is a five-minute look at a dashboard, not a new observability rollout. - **One-command rollbacks**, so a bad change costs you a rollback, not an incident. You still own the architectural decisions. Ownkube keeps the primitives in place so the drift has fewer places to hide. ## A worked example Take a typical 2026 SaaS running Kubernetes on AWS: 1 cluster, ~24 vCPU production fleet, 8 vCPU staging, 10 active preview environments, RDS Postgres, ElastiCache. **Before optimization**: ~$2,400/month cluster compute + $180 NAT + $90 EBS = **$2,670/month**. **After applying all seven patterns**: | Pattern | Saving | |---|---| | Right-sizing (Pattern 1) | -$840 | | Spot capacity (Pattern 2) | -$520 | | Idle environment sleep (Pattern 3) | -$280 | | Namespace quotas + governance (Pattern 4) | -$120 | | Private image registry (Pattern 5) | -$140 | | NAT routing audit (Pattern 6) | -$110 | | Storage class right-sizing (Pattern 7) | -$30 | | **Total saved** | **-$2,040** | **After**: ~$630/month. About a 76% reduction on the cluster bill. Numbers are illustrative for a defined workload. Your savings will vary. ## Decision checklist Before you start, confirm: - [ ] Do you have a way to measure current per-workload resource usage (Prometheus, CloudWatch, Datadog)? - [ ] Is your cluster on Kubernetes 1.27+ (so VPA and Karpenter work cleanly)? - [ ] Do you have at least one engineer with a couple of weeks of focused time? - [ ] Is anyone going to own ongoing cost vigilance after the initial pass, or do you need that on autopilot? If you ticked all four, you're set up to do the work in-house. If you ticked three or fewer, consider a platform layer that runs these patterns by default. ## Closing Kubernetes cost optimization at a small startup isn't a mysterious art. It's seven well-understood patterns plus one structural change to stop the drift from coming back: making the platform itself carry the defaults instead of leaving them to memory. If you'd rather skip the initial work and start with a platform that ships those defaults, Ownkube runs inside your own AWS account. Starter is free forever on one instance, and Production is [$5 per vCPU plus $1 per GiB RAM per month](/pricing) once you scale. [Deploy your first app](https://app.ownkube.io/login). --- ## Fly.io alternative in 2026: moving to your own AWS > Why teams leave Fly.io in 2026 (regions, compliance, AWS credits, predictable bills) and what moving to your own AWS account looks like. - Canonical: https://ownkube.io/blog/fly-io-alternative-own-aws - Markdown: https://ownkube.io/blog/fly-io-alternative-own-aws.md - Published: 2026-05-15 - Author: Abhishek Jaiswal - Category: Engineering - Tags: fly-io-alternative, platform-engineering, aws, self-hosted, heroku-alternative [Fly.io](https://fly.io) is one of the best platform products of the last five years. The Firecracker microVMs are fast, the region story is real, the developer experience is sharp, and the team has built a genuinely opinionated take on what "global by default" should mean. For a lot of small projects in 2026, Fly is still our recommendation. This post is for the teams who've outgrown it. The triggers tend to be specific (a customer asking for AWS-only data residency, an AWS Activate balance you're not capturing, a bill that's drifted into uncomfortable territory), and the next move isn't obvious. We'll cover where Fly stops fitting, who should stay, what your AWS-native alternative actually looks like, and the honest cost of the migration. **Skim answer:** - **Why teams leave Fly:** customers require AWS data residency, they qualify for AWS Activate credits they can't redeem on Fly, their Fly bill crosses $1,500 a month with surprises, or they hit Fly-specific scaling and observability limits. - **2026 alternative most small teams pick:** run on their own AWS account at wholesale rates with a platform layer (e.g. Ownkube) on top. - **What you keep:** the deploy ergonomics they liked on Fly. ## Where Fly.io is still the right call Be fair to the incumbent first. Fly remains the strongest choice when: - You need genuinely low-latency reads in 5+ regions and your app architecture supports it (Litestream, Fly Postgres with read replicas in-region, region-aware request routing). - Your team is 1 to 5 engineers and you don't want to learn AWS. - Your workload is naturally stateful at the application layer (game servers, real-time collaboration, anything where stickiness matters) and Fly's "place this VM here" model is a feature, not a bug. - You don't qualify for, or aren't going to use, AWS Activate credits. If two or more describe you, stay on Fly. Stop reading. ## Why teams eventually leave The migration triggers we see in 2026 are remarkably consistent. **Trigger 1: AWS credits you can't capture.** A funded startup with up to $100,000 in AWS Activate credits and a Fly bill is, mathematically, lighting half the credits on fire. We wrote up the underlying math in our [AWS Activate credits guide](/blog/aws-activate-credits-guide-2026). The short version: credits redeem against AWS spend, not Fly bills, and the validity window is 12 to 24 months. **Trigger 2: Customer or compliance requires AWS specifically.** SOC 2, HIPAA, and most enterprise procurement are doable on Fly's infrastructure, but the path is shorter on AWS. When a customer's security team says "we need your workloads in our region of AWS, under our KMS keys", Fly is the wrong substrate. **Trigger 3: The bill drifts.** Fly's metered model is great when traffic is small. It surprises you when traffic spikes, when you accidentally pin a VM that should have been autoscaled-down, or when egress costs compound. Several teams in our network report unexpected $3K to $8K monthly Fly bills that, when migrated, ran $700 to $1,800 on wholesale AWS. **Trigger 4: Operational limits in 2026.** Fly's observability story is improving but still trails AWS-native (CloudWatch, OTel, X-Ray). Multi-region Postgres on Fly is good for some patterns and awkward for others. Some teams report tail-latency variance that AWS reserved capacity doesn't have. If three or more of these apply, the move is usually worth the migration cost. ## What the AWS-native alternative looks like The basic shape: - **Compute**: EKS (multi-AZ) or k3s on EC2, depending on team size and traffic. - **Database**: Managed RDS Postgres in the same region (Multi-AZ for production). For multi-region read replicas, use RDS cross-region replication. - **Caching**: ElastiCache (Redis or Memcached). - **Edge**: Cloudflare or CloudFront in front of a load balancer, for managed DNS and edge TLS. - **Storage**: S3 for artifacts, build cache, and large objects. - **Secrets**: AWS Secrets Manager with workload identity via IRSA on EKS. - **Observability**: CloudWatch + OTel, optionally fanned out to Datadog/Honeycomb/etc. The deploy ergonomics that made Fly nice (git push, a ready-to-share URL, the abstraction over individual VMs) come from the platform layer on top, not from AWS directly. That's the part most teams underweight. ## Two roads to that alternative Two practical paths exist. ### Road 1: Stand it all up yourself with a [DevOps engineer](/blog/devops-engineer-salary-cost-2026) You hire (or borrow) a platform engineer, they build the cluster, configure the deploy pipeline, wire up observability, stand up preview environments, design the secrets story. **Time to production**: 4 to 12 weeks for a competent engineer, longer if they're learning. **Loaded cost**: ~$200K/year for the hire, plus tooling licenses, plus your engineering attention. We wrote about that loaded cost in our [DevOps engineer salary breakdown](/blog/devops-engineer-salary-cost-2026). **Verdict**: appropriate if you're 25+ engineers and the platform engineer has follow-on work after the migration. Heavy if you're smaller. ### Road 2: Use a managed platform layer that runs in your AWS account A product like [Ownkube](https://ownkube.io) installs in your AWS account, provisions the cluster, wires up the deploy pipeline, gives every app a ready-to-share URL, and sets up secrets and observability: - **Builds and deploys**: a push triggers a container build and a rollout, with a health check and an automatic rollback if it fails. - **TLS and a ready-to-share domain**: an `ownkube.app` address with automatic HTTPS out of the box, plus your own domain on a Starter cluster when you're ready. - **Logs and metrics**: centralized from the start, no separate agent to wire up. - **Managed Postgres and autoscaling**: the database is operated for you, and workloads scale horizontally with traffic. **Time to production**: hours to days for a small team. Connect the AWS account, point the platform at your git repo, deploy. **Cost**: free on BYOC Starter (one AWS instance, fits side projects and small-team production). $5 per vCPU plus $1 per GiB RAM per month on BYOC Production when you scale, paid to AWS directly at wholesale. **Verdict**: appropriate if you're 5 to 30 engineers and you want the migration to take days, not months. ## A worked example A small SaaS on Fly with: 1 web service, 2 background workers, Fly Postgres, multi-region read replica, a handful of preview environments, ~3 TB/month egress. **On Fly.io (2026 metered pricing)**: approximately $1,800 to $2,800 per month. **On AWS at wholesale (EKS multi-AZ + RDS Multi-AZ + ElastiCache + Cloudflare edge)**: approximately $700 to $1,100 per month in AWS spend, fully redeemable against Activate credits. **Platform layer on top**: $0 on BYOC Starter (if traffic fits) or ~$150 to $250 on BYOC Production for this footprint. Year-1 cash impact for a credit-funded startup: from ~$24K out the door on Fly to under $3K on the AWS-native setup with Ownkube on top. ## The migration is real work, but bounded A common myth is that leaving Fly means months of rewrites. For most small teams the actual work is bounded: - **Containerize what isn't already**. Most Fly apps are already containers. If not, this is a half day. - **Move the database**. RDS Postgres logical replication or `pg_dump` + restore. For a 30 GB Postgres, plan on 4 to 8 hours including verification. - **Re-point DNS**. Lower the TTL a few hours ahead, then switch the record. Half day. - **Re-wire CI**. Push to a different registry, deploy to a different cluster. Half day. - **Validate the deploy pipeline and observability**. One day of testing. Total elapsed time for a small team: 1 to 2 weeks of focused work. Not free, but not the multi-month project some make it out to be. ## When NOT to migrate Stay on Fly if: - You're under $500/month of Fly spend. - Your application architecture genuinely benefits from Fly's per-VM region placement model. - You don't have AWS Activate credits and aren't on a compliance path that requires AWS. - Your team has 1 to 3 engineers and the operational simplicity is worth more than the bill delta. The argument is not "Fly is bad". The argument is "the math has flipped for a specific class of team". ## Decision checklist - [ ] Is your Fly bill over $1,500/month? - [ ] Do you have AWS Activate credits or GCP credits you're not redeeming? - [ ] Is a customer or compliance requirement pushing you toward AWS specifically? - [ ] Are you 5+ engineers and growing? - [ ] Do you need observability or networking primitives Fly doesn't expose? Three or more yeses: the migration usually pays back inside the first 90 days. **Reference:** For the side-by-side breakdown, see [Ownkube vs Fly.io](/docs/compare/fly) in the docs. ## Closing Fly.io is still one of the best platform products of its generation, and we're not in the business of arguing otherwise. The honest 2026 reality is just that some teams outgrow the model, and the next stop isn't another shared PaaS. It's your own AWS account, with a platform layer that gives you the deploy ergonomics you liked. If that's the move you're sketching, Ownkube is built for the migration. BYOC Starter is free forever on one AWS instance; BYOC Production is $5 per vCPU plus $1 per GiB RAM per month when you scale, paid to AWS directly at wholesale. If you'd rather skip the AWS account for now, Ownkube Compute gets you a live URL in seconds from [$5 a month](/pricing). [Deploy your first app](https://app.ownkube.io/login). --- ## AWS spot instances in production: a 2026 playbook > Run production on AWS spot instances safely in 2026: interruption handling, fallback patterns, realistic savings, and what to never put on spot. - Canonical: https://ownkube.io/blog/aws-spot-instances-production-guide - Markdown: https://ownkube.io/blog/aws-spot-instances-production-guide.md - Published: 2026-05-13 - Author: Abhishek Jaiswal - Category: Engineering - Tags: aws-spot-instances, aws-cost, cloud-cost, ec2, kubernetes The cheapest EC2 capacity AWS sells is also the one most teams refuse to use. Spot instances run at 60 to 80% off on-demand pricing in 2026 (depending on instance family and region), and a healthy share of workloads at every startup we've audited could safely run on them. The reason most teams don't: a vague memory of an interruption story from 2017, and no clean pattern for handling reclaim events. **Skim answer:** - **What they are:** EC2 capacity AWS reclaims when on-demand demand spikes. - **What they cost in 2026:** 60 to 80% less than on-demand. - **Safe for:** stateless web pods, queue consumers, build runners, batch jobs, and most preview environments. - **Unsafe for:** stateful primaries (Postgres, Redis), control planes (Kubernetes masters), and any single-replica workload with hard SLAs. - **Right answer for most small teams:** mixed. On-demand for the few stateful things, spot for everything else. This post is the playbook for that split. ## How spot pricing actually works in 2026 AWS sells the same EC2 capacity in three flavors: | Type | Price (relative) | Reclaim behavior | |---|---|---| | On-demand | 100% | None. Yours until you stop it. | | Savings Plans / RIs | 50 to 70% of on-demand | None. Pre-committed for 1 or 3 years. | | Spot | 20 to 40% of on-demand | AWS can reclaim with 2 minutes notice. | Spot pricing in 2026 is largely steady. The wild 5-minute price swings of 2018 are gone; today most instance families show stable spot prices within a band, with occasional reclaim events during regional capacity crunches. Real numbers from `us-east-1` in April 2026 (approximate, varies by hour): | Instance | On-demand $/hr | Spot $/hr | Savings | |---|---|---|---| | t3.xlarge | $0.166 | $0.045 | 73% | | m6i.large | $0.096 | $0.027 | 72% | | c7g.large | $0.072 | $0.022 | 69% | | r6i.large | $0.126 | $0.034 | 73% | | g5.xlarge (GPU) | $1.006 | $0.291 | 71% | The spot discount is real. The question is operational: what workload can tolerate a 2-minute eviction notice without breaking a customer experience? ## What's safe on spot Workloads that handle interruption gracefully: - **Stateless web pods behind a load balancer**. The load balancer drains an evicted pod; another pod on a different node takes over. As long as you have more than one replica and the cluster has on-demand fallback capacity, the customer sees nothing. - **Queue consumers**. A worker that's halfway through a message either finishes (2 minutes is usually enough) or the message returns to the queue and another worker picks it up. Design for idempotency, which you should be doing anyway. - **Build runners**. Github Actions self-hosted runners, GitLab runners. If a build dies, retry. The cost saving on a busy CI fleet is significant. - **Batch jobs and cron**. Same logic. Idempotent batch jobs survive interruption. Non-idempotent jobs need to be made idempotent before going on spot. - **Preview environments**. Per-PR environments are by definition transient. Spot is perfect for them. - **ML training and inference at large scale**. Most training frameworks support checkpointing; modern inference is multi-replica behind a load balancer. ## What's unsafe on spot These should stay on on-demand or reserved capacity: - **Database primaries**. Postgres, MySQL, MongoDB, Redis. A 2-minute notice is not enough to safely fail over a database. Use spot for read replicas, not the primary. - **Stateful services with no replication**. Single-replica workloads of any kind. - **Kubernetes control plane nodes**. If you're running k3s on EC2 (instead of managed EKS), keep the control-plane node on on-demand. - **Anything with a hard latency SLA**. The reclaim event introduces a brief disruption. If your SLA is "99.99% under 50ms p99", spot adds variance you can't afford. - **Workloads with long stateful operations**. A video transcoder mid-job, a long-running data migration, anything that loses progress when interrupted. ## The patterns that make spot safe Three patterns turn a "spot is too scary" team into a "we run mostly on spot" team: ### Pattern 1: Mixed instance pools with on-demand fallback Configure your autoscaling group or Karpenter (on EKS) with a base of on-demand capacity plus spot for the rest. The on-demand base absorbs spot reclaim events while replacement spot capacity provisions. A typical small-startup ratio: 25% on-demand, 75% spot. For a 8-vCPU production fleet, that's 2 vCPU of on-demand always there, with 6 vCPU of cheaper spot capacity layered on top. If spot is reclaimed, the workload continues on the on-demand base while new spot capacity comes online (typically under 5 minutes). ### Pattern 2: Diversified instance families and AZs Spot reclaim events are usually scoped to a specific instance family in a specific AZ. If your pool is "any of m6i.large, m6a.large, c6i.large, c6a.large across 3 AZs", a reclaim event on one type rarely takes the whole pool down. Both `eksctl` and `karpenter` make this trivial. Configure 4 to 6 instance types in the same broad family (general-purpose, compute-optimized, memory-optimized) and let the scheduler pick. ### Pattern 3: Pod disruption budgets and graceful shutdowns In Kubernetes, set [`PodDisruptionBudgets`](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/) so the cluster knows the minimum-available count for each workload. When a spot node is reclaimed, Kubernetes drains pods according to those budgets, giving each container a `terminationGracePeriodSeconds` window to finish what it's doing. For most web pods, 30 seconds is enough. For queue consumers processing a single message, 90 to 120 seconds is closer to right. The spot notice is 2 minutes, so design termination grace to fit inside it. ## A worked example Take a typical 2026 SaaS: 1 web service (3 replicas), 2 background workers (2 replicas each), 5 preview environments, build runners, RDS Postgres (Multi-AZ) and ElastiCache (managed AWS, untouched by this exercise). | Workload | Replicas | Spot strategy | Approx. monthly EC2 savings | |---|---|---|---| | Web pods | 3 | 1 on-demand, 2 spot | $120 | | Worker pods | 4 | All spot | $200 | | Preview envs | 5 | All spot | $180 | | Build runners | variable | All spot | $80 | | Control plane (k3s) | 1 | On-demand | $0 (baseline) | **Total**: roughly $580 per month saved on a base compute bill of ~$800. About 70% off on a workload that's already pretty modest. For a Series A SaaS pushing 50+ vCPU of production traffic, the saving compounds into mid-five figures annually. ## When NOT to bother Be honest with yourself. - **Very small workloads** (1 to 2 small instances, under $100/month total) don't move the needle. The engineering time to configure spot exceeds the saving. - **Pre-product-market-fit teams** should not optimize compute cost. Ship features. Optimize when growth makes the bill visible. - **Workloads governed by deeply regulated SLAs** (financial trading, real-time medical) shouldn't introduce spot variance. ## How the platform layer handles this A point worth making: spot configuration is the kind of work nobody on a 5- to 20-person team gets around to doing well. The instance-family diversification, the on-demand fallback, the pod disruption budgets, they sit on someone's "next quarter" list for years. If you'd rather not own that list, [Ownkube](https://ownkube.io) runs the same workloads inside your own AWS account, with builds, deploys, TLS, and horizontal autoscaling handled by the platform, plus logs and metrics on every deploy so you can see what's actually happening. The spot decisions above are still yours to make; the platform just gives you a shorter path to acting on them. You don't have to build the deploy pipeline and observability yourself. That part's the default. ## Decision checklist Before flipping a workload to spot: - [ ] Does the workload have more than one replica behind a load balancer or queue? - [ ] Is the workload idempotent (or made idempotent) so an interruption doesn't corrupt state? - [ ] Have you configured `terminationGracePeriodSeconds` (or equivalent) inside the 2-minute spot notice window? - [ ] Do you have an on-demand fallback layer that absorbs reclaim events? - [ ] Is your monitoring set up to alert on actual customer impact (not just "node went away")? Five yeses, go to spot. Any no, fix that first. ## Closing Spot is one of the highest-leverage cost moves on AWS in 2026, and it's massively underused by small teams. The pattern is well-understood: mixed pools, instance diversification, graceful shutdowns, and an on-demand base. With those four in place, 60 to 80% off compute is a real saving, not a paper exercise. ## Related reading - [Kubernetes cost optimization for startups](/blog/kubernetes-cost-optimization-startups) - [AWS NAT gateway cost: why it's high and the fix](/blog/aws-nat-gateway-cost-fix) - [EKS vs k3s on AWS: cost and when to choose](/blog/eks-vs-k3s-on-aws-for-startups) If you'd rather build on a platform that ships builds, deploys, TLS, and autoscaling as defaults instead of separate projects, Ownkube runs the same workloads inside your own AWS account. Starter is free forever on one instance; Production is [$5 per vCPU plus $1 per GiB RAM per month](/pricing) once you scale. [Deploy your first app](https://app.ownkube.io/login). --- ## Internal developer platform: when you need one > A plain 2026 definition of internal developer platforms, four signals your team needs one, and the build-vs-buy framework most startups use. - Canonical: https://ownkube.io/blog/internal-developer-platform-2026 - Markdown: https://ownkube.io/blog/internal-developer-platform-2026.md - Published: 2026-05-11 - Author: Abhishek Jaiswal - Category: Engineering - Tags: internal-developer-platform, platform-engineering, developer-experience, devops "Internal developer platform" (IDP) is the most overloaded term in the 2026 infrastructure conversation. Some people mean a Backstage portal. Some mean a Kubernetes setup with golden paths. Some mean a custom-built layer on top of AWS that abstracts away the rough edges. All of those are correct. None of them are useful when you're a 10-person team trying to decide what to actually do. This post is the operator-level definition. We'll cover what an IDP is in plain English, the four signals that say your team has crossed the line where one would help, the three common shapes IDPs take in the wild, and a build-vs-buy framework that most growing startups settle into. **Skim answer:** - **What it is:** software that gives engineers a self-serve way to deploy applications, run services, and access infrastructure without filing tickets or memorizing cloud-vendor consoles. - **Who doesn't need one:** a 5-engineer team. - **Who almost always does:** a 25-engineer team. - **The crossover:** around 10 to 15 engineers, when ad-hoc DevOps starts taxing senior engineers' weeks. ## What an IDP actually is Strip away the conference-talk language and an IDP is a layer with five jobs: 1. **A self-serve deploy path**. An engineer pushes code, the platform builds and deploys it. No tickets, no Slack DMs to the one person who knows how the cluster works. 2. **Golden paths**. Opinionated templates for common patterns: a web service, a worker, a cron, a database, a queue. New services start from the template, not from a blank YAML file. 3. **Environment management**. Production, staging, and preview environments per pull request, on demand. 4. **Observability and access**. Logs, metrics, and SSO into running containers, available to the engineer who owns the service, not gatekept by a platform team. 5. **Guardrails**. Policies that prevent obvious mistakes (a workload without resource limits, a public S3 bucket, a secret in plain text) without blocking velocity. Notice what's not on the list. An IDP is not a Kubernetes cluster. Kubernetes is one possible substrate. An IDP is not Backstage. Backstage is one possible portal. An IDP is not a custom Terraform monorepo. That's one possible implementation. The IDP is the **experience** the engineer has, regardless of what's underneath. ## The four signals that say you need one We see startups try to spec out an IDP project for two reasons: a real signal that the current ad-hoc setup is breaking, or a vanity project ("Stripe has Backstage so we should have Backstage"). The second is expensive. Watch for the first. **Signal 1: Senior engineers are spending more than a day a week on deploys, alerts, and infra tickets.** This is the canonical break-even moment. The cost of building or buying a platform is justified when you have measurable engineering time being burned on recurring ops. **Signal 2: New hires take more than a week to ship their first production change.** If the answer to "how do I deploy this?" requires a half-day onboarding session, the platform is the bottleneck. A good IDP makes this a 30-minute walkthrough. **Signal 3: You have more than two flavors of "how to deploy a service" in production.** Some services use one set of scripts, some another, some are deployed by hand. This is a leading indicator of a real incident waiting to happen. **Signal 4: Compliance is asking who can access what, and you can't answer in less than an hour.** SOC 2, HIPAA, and enterprise procurement all want auditable, policy-enforced access. An IDP centralizes the answer. If two or more apply, you're past the line. If only one, you can probably hold off another quarter and revisit. ## The three shapes IDPs take in 2026 In the wild, IDPs land in one of three patterns. None of them is wrong; they're each right for a different team size and stage. ### Shape 1: The custom-built monorepo A platform team builds and operates the IDP in-house. Usually on top of Kubernetes (EKS for AWS shops), with Terraform or Pulumi for cloud resources, ArgoCD or Flux for GitOps, Backstage or a custom portal for developer self-serve, OPA or Kyverno for policy. **Best for**: large engineering organizations (100+ engineers) where the platform team can be 5+ people, and where the business value of a perfectly bespoke developer experience justifies the multi-year roadmap. **The honest cost**: a dedicated platform team of 4 to 8, plus tooling licenses, plus the ongoing maintenance burden as Kubernetes versions, AWS services, and security policies evolve. ### Shape 2: The Backstage-style portal on existing infrastructure A smaller team adopts Backstage (or similar) as a service catalog and developer portal, on top of whatever cloud infrastructure they already run. **Best for**: mid-size organizations (30 to 100 engineers) that already have a platform engineering function and want a unified front-end across heterogeneous backends. **Where it stops fitting**: Backstage is a portal, not a platform. It doesn't deploy your services for you. Underneath, you still need the platform substrate. For most startups, the cost of building the substrate is the actual work. ### Shape 3: A platform layer that already exists, on your cloud account A managed product that runs in your own AWS, GCP, or Azure account and gives you the self-serve deploy, golden paths, environment management, observability, and guardrails out of the box. You don't operate the platform; you use it. **Best for**: 5- to 50-engineer startups that want IDP-level developer experience without standing up a platform team to build it. **Where it stops fitting**: if you have genuinely bespoke infrastructure that no managed product abstracts (real-time streaming on custom hardware, regulated workloads with unusual constraints), you'll end up customizing the platform layer or running parallel custom infrastructure. ## The build vs buy framework When teams ask us "should we build or buy", the framework we use: | Question | Build | Buy | |---|---|---| | Team size | 30+ engineers | 5 to 30 engineers | | Dedicated platform owners | 3+ committed for 18+ months | Fewer than 3, or part-time | | Cloud surface | Multi-cloud, heterogeneous | Single cloud (AWS, GCP, or Azure) | | Workload uniformity | Highly diverse (real-time, ML, batch, web) | Standard web, workers, cron, DB | | Compliance pressure | Hard requirements (FedRAMP, regulated industries) | Standard (SOC 2, HIPAA) | | Time to value | Can afford 12 to 18 months | Need IDP-level DX inside one quarter | | Strategic differentiation | DX is a competitive advantage | DX is necessary, not differentiating | Most 2026 startups land firmly on the "buy" side of every row. The honest reason most "build" projects start is that one senior engineer wants to design a platform. That's a fine motivation if you have the size and runway. If you don't, it ends with a half-finished platform and a smaller team. ## What the automated version looks like A point that's specific to 2026: the build-vs-buy question now has a third option that didn't exist three years ago. You can buy a platform layer that already handles the recurring operational work the platform produces. At [Ownkube](https://ownkube.io) that covers: - **Self-serve deploys.** Push a repo or a container, get a build pipeline, a zero-downtime rollout, and a one-command rollback, on hosted infrastructure or inside your own AWS account. - **A ready-to-share domain and TLS.** An `ownkube.app` address with automatic HTTPS on day one, with no DNS to set up. - **Managed Postgres and Valkey cache.** Operated for you, the same on Ownkube Compute and in your own cloud. - **Logs, metrics, and horizontal autoscaling.** One place to see what's running, with replica counts that adjust to traffic automatically. This doesn't replace strategic platform decisions. It covers the recurring checklist that would otherwise either fall on a senior engineer or push you to hire a [DevOps engineer at ~$200K loaded](/blog/devops-engineer-salary-cost-2026). For a 5- to 30-engineer team, that's the gap that makes the platform layer credible as a substitute for hiring a platform team. ## When you genuinely don't need an IDP Be honest with yourself. If you're 5 engineers, you ship a couple of services, your AWS bill is under $1,000 a month, and nothing in the four signals applies to you, **you don't need a platform**. You need a deploy script and an alerting integration. The temptation to over-engineer at small team size is real and expensive. Revisit the question every quarter. The signals usually trip before you expect them to. ## Decision checklist - [ ] Are senior engineers spending more than a day a week on deploys and infra tickets? - [ ] Is onboarding a new hire to production taking more than a week? - [ ] Are there more than two ways to deploy a service in production today? - [ ] Is compliance pressing on access control and audit trails? - [ ] Are you on AWS specifically (because of credits, compliance, or customer requirements)? - [ ] Is your team between 5 and 30 engineers without a dedicated platform owner? Two or more on the first four: you need an IDP. Yes on the last two: you almost certainly want to buy, not build. ## Closing The IDP conversation in 2026 is no longer about whether your team needs one. For most growing startups, the moment is going to arrive between engineers 10 and 25. The conversation is about whether you'll build it, portal-stack it, or buy a managed layer that already exists. If you want a managed IDP that runs in your own AWS account, handles the recurring ops, and starts free on a Starter instance, Ownkube is built for that. Or skip the cloud account entirely and ship on Ownkube Compute. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## Self-hosted PaaS in 2026: Coolify, Dokku, CapRover > Comparing self-hosted PaaS options for small teams who want Heroku-style DX without the markup: setup, scale ceiling, ops burden, and limits. - Canonical: https://ownkube.io/blog/self-hosted-paas-comparison-2026 - Markdown: https://ownkube.io/blog/self-hosted-paas-comparison-2026.md - Published: 2026-05-08 - Author: Abhishek Jaiswal - Category: Engineering - Tags: self-hosted-paas, coolify, dokku, caprover, heroku-alternative, platform-engineering A self-hosted PaaS sounds like the obvious fix to a marked-up Heroku bill. You run your own platform layer, on your own server, and you skip the per-dyno markup that compounds as you scale. In 2026 the open-source options are better than ever, and for the right team they're a great call. For the wrong team they quietly become a second job. This post compares the four self-hosted and "self-hosted-style" PaaS options we see startups evaluating most often: [Coolify](https://coolify.io), [Dokku](https://dokku.com), [CapRover](https://caprover.com), and [Ownkube](https://ownkube.io). We'll cover what each one is, who it fits, where the ceiling is, and the honest tradeoffs. If you're trying to leave Heroku specifically, our [Heroku alternative guide](/blog/heroku-alternative-in-your-own-aws-account) covers the workflow translation in more detail. ## TL on who picks what - **Indie developer, side project, single server, budget under $20/month**: Dokku or CapRover. Both will run happily on a single $10 VPS and both have an active community. Pick Dokku if you want raw Heroku-buildpack DNA. Pick CapRover if you want a click-through UI. - **Solo founder or small team, want a clean UI and a wider service catalog**: Coolify. The 2024 to 2026 push made it the prettiest option, and it's added enough first-class services (databases, queues, monitoring) to feel like a real platform. - **Team of 5 to 20 engineers, on AWS, expecting to scale, wants no DevOps hire**: this is where Ownkube fits. Same self-hosted spirit (runs in your own cloud account), but with a managed control plane, and Ownkube operates the platform and automates the recurring ops so you don't have to. The rest of this post is the detail behind that call. ## What "self-hosted PaaS" really means Before the comparison, it's worth being clear about what the category is. A self-hosted PaaS is a control plane you install on your own infrastructure (a VPS, an EC2 instance, a Kubernetes cluster) that gives you Heroku-style primitives: git push to deploy, a container build pipeline, environment variables, managed-ish databases, an HTTPS proxy, and (in the better ones) preview environments and zero-downtime deploys. The shared promise is the same as Heroku: you push code, you get a URL. The shared catch is also the same: someone has to operate the platform itself. The four products below split on **how much of that operation is software vs. you**. ## The comparison table | | Coolify | Dokku | CapRover | Ownkube | |---|---|---|---|---| | License | Open source (Apache 2) | Open source (MIT) | Open source (Apache 2) | Commercial, runs in your cloud account | | Underlying engine | Docker + custom orchestrator | Docker + Heroku buildpacks | Docker Swarm | Kubernetes, managed for you | | Minimum viable host | 1 VPS, 2 GB RAM | 1 VPS, 1 GB RAM | 1 VPS, 1 GB RAM | None on Compute; 1 EC2 instance in your own cloud | | Multi-node scale path | Limited, single-node primary | Limited, single-node | Docker Swarm scaling, fragile beyond a few nodes | Multi-AZ in your own AWS when you graduate | | UI quality | Strong, modern dashboard | Minimal, CLI-first | Mid, click-through UI | Modern dashboard, same on hosted and your own cloud | | Managed databases | First-class, several engines | Plugin-based, mature Postgres plugin | Plugins, less polished | Managed Postgres, hosted or in your own AWS | | Preview environments per PR | Yes, beta | No, you script it | Limited | On the roadmap | | TLS / DNS | Caddy + Let's Encrypt, you manage DNS | Let's Encrypt, you manage DNS | Let's Encrypt, you manage DNS | Automatic HTTPS on a ready-to-share URL, no DNS to configure | | Built-in ops automation | None | None | Limited | Builds, deploys, TLS, logs, and rollbacks managed | | Best fit team size | 1 to 5 | 1 to 3 | 1 to 5 | 1 to 20 on Compute, 20 to 100 in your own cloud | | Ops burden on you | High | High | High | Low | Anything not on this table is a rounding error for the buyer decision. ## Coolify: the prettiest open-source option [Coolify](https://coolify.io) had the strongest year of any self-hosted PaaS in 2024 to 2025 and rolled into 2026 with a sharper UI, a real preview-environments feature, and a service catalog that feels modern. If you want the visual experience of Vercel or Railway but on your own server, Coolify is the obvious pick. **Strengths.** Clean dashboard. First-class managed services (Postgres, MySQL, MongoDB, Redis, MinIO). Active community. Generous free self-host tier. **Where it stops fitting.** Coolify is at its best on a single server or a small handful. Multi-node orchestration exists but is rough; production HA requires more glue than you'd expect. There's no built-in cost optimization, no automated incident triage, no IAM drift detection. The platform gives you primitives; the operations are still yours. **Pick Coolify if** you're 1 to 5 engineers, you want a slick UI, and you're comfortable owning the platform yourself. ## Dokku: the durable Heroku clone [Dokku](https://dokku.com) is the OG. Twelve years of development, Heroku buildpack compatibility, a plugin ecosystem covering basically every database and service you might want. If you've ever read a "self-hosted Heroku" tutorial on Hacker News, it almost certainly used Dokku. **Strengths.** Rock-solid on a single host. Buildpacks mean your Heroku-style app code just works. Tiny resource footprint. Predictable behavior. Mature Postgres plugin with backups. **Where it stops fitting.** Single-node by design. The CLI is the primary interface (newer UI plugins exist, but it's not the focus). Preview environments require scripting. No first-class observability or cost tooling. **Pick Dokku if** you're a solo developer, you live in the terminal, and you want the most boring, predictable thing that runs ten years from now. ## CapRover: the click-through middle ground [CapRover](https://caprover.com) sits between Dokku and Coolify. It's built on Docker Swarm, ships with a web UI, supports one-click apps from a community store, and runs happily on a $10 VPS. **Strengths.** Easy to install (one shell command). Active app store with prebuilt deployments. Friendly to non-DevOps founders who want a clickable interface. **Where it stops fitting.** Docker Swarm is the underlying orchestrator, and Swarm has been in maintenance mode for years. Multi-node clusters work but the failure modes are quiet and painful. Less momentum than Coolify in the recent UI race. **Pick CapRover if** you want a click-through UI on a single server, you don't mind that the engine underneath is Swarm, and you're not planning to scale past a handful of nodes. ## Ownkube: the team-scale path [Ownkube](https://ownkube.io) is a different shape. You can deploy on Ownkube Compute, our hosted infrastructure, with no cloud account at all, or connect an AWS account and have Ownkube provision and operate the same platform inside it. Two co-equal on-ramps, same workflow and dashboard: - **Ownkube Compute (from a $5 top-up).** Push a repo or a container and get a live URL in seconds. No cloud account, no config, no subscription. Billed on a prepaid wallet: top up from $5, or pick a plan (Personal loads $5 of credit a month and Team loads $20), and per-minute usage draws it down at about $0.00041 per vCPU-minute and $0.0001 per GiB-minute, roughly a third under the equivalent Railway bill. Best for indie projects, side projects, and small-team production. - **Bring your own cloud.** Run the same workloads inside your own AWS account. Starter is free forever on one instance; Production is $5 per vCPU plus $1 per GiB RAM per month, and you pay AWS directly at wholesale. Best for growing teams that want their own data boundary and their cloud credits. **Strengths.** - **No DNS to configure.** Every app gets an `ownkube.app` address with automatic HTTPS on day one. No nameserver hand-off, no waiting for propagation. - **Any workload from one push.** Web apps, background workers, cron jobs, and managed Postgres. If it runs in Docker, it runs on Ownkube. - **Managed Postgres and Valkey cache.** Operated for you on both Compute and your own cloud, so nothing changes when you move. - **Vanilla infrastructure underneath.** Your account, your data, your KMS keys. Disconnect anytime and the workloads keep running. - **AWS Activate credits land at wholesale.** No platform markup between you and EC2. **Where it stops fitting.** Ownkube is AWS-first for bring-your-own-cloud today (GCP and Azure on the roadmap). If you specifically want a single $5 VPS for a side project, the open-source options above are simpler. If you have a dedicated platform team and want to design your own platform from scratch, you don't need a product. **Pick Ownkube if** you're 1 to 20 engineers, you don't want to hire a [DevOps engineer at $200K loaded](/blog/devops-engineer-salary-cost-2026), and you want to ship in seconds on hosted infrastructure with a clean path into your own AWS as you grow. ## The hidden tradeoff of pure self-hosting Open-source self-hosted PaaS is genuinely free at the license level. The cost shows up elsewhere. Specifically: - **Patching the platform itself.** Coolify, Dokku, and CapRover ship updates regularly. Some are security-critical. You're on the hook for applying them. - **Backup and restore drills.** Plugin-managed Postgres has backups, but verifying restore on a new node is your job. - **Failure modes during traffic.** The platform processes that run your platform (the control plane, the reverse proxy) need their own observability. Otherwise the first sign of trouble is downtime. - **Migration when you outgrow it.** Most self-hosted PaaS options have a clear single-node ceiling. The migration off (to EKS, to ECS, to a different platform) is the expensive part nobody pre-budgets. This is why Ownkube runs vanilla Kubernetes underneath: when you graduate from Ownkube Compute to your own AWS account, it's the same container workflow, the same platform, the same dashboard. There's no migration. You graduate. ## Decision checklist Use this to pick: - [ ] How big is the team today? (1 to 3, 4 to 10, 10+) - [ ] What's the maximum traffic you expect in the next 12 months? - [ ] Do you want to own platform operations, or have software own them? - [ ] Are you on AWS specifically (because of credits, compliance, or customer requirements)? - [ ] What's your DNS / TLS comfort level? If the team is small, single-server fits, and you're happy to own ops: Coolify, Dokku, or CapRover. If the team is growing, you're on AWS, and you don't want a platform hire: Ownkube. ## Closing Self-hosted PaaS is a real category in 2026, and for indie developers it's better than ever. Coolify, Dokku, and CapRover all earn their place. The honest gap they share is that you're still the operator. For a 1- to 20-person team, the win is to keep the self-hosted spirit (your account, your data, your credits at wholesale) but let software handle the recurring ops. That's the shape Ownkube takes. Ship in seconds on Ownkube Compute from [$5 a month](/pricing) with nothing to set up, or run in your own cloud: free on a Starter instance, $5 per vCPU + $1 per GiB RAM when you scale to Production. [Deploy your first app](https://app.ownkube.io/login). --- ## Heroku in 2026: the real cost of staying > Heroku's 2026 pricing changes, the per-dyno math at typical small-team scale, and a side-by-side of what the same workload runs at in your own AWS account. - Canonical: https://ownkube.io/blog/heroku-2026-pricing-alternatives - Markdown: https://ownkube.io/blog/heroku-2026-pricing-alternatives.md - Published: 2026-05-06 - Author: Abhishek Jaiswal - Category: Engineering - Tags: heroku-pricing, heroku-alternative, paas-cost, aws, platform-engineering If you've been on Heroku since the Salesforce era and haven't reviewed the bill in a year, this is the post for you. The 2026 pricing surface is meaningfully different from 2023. The free Eco dyno is gone. Performance dyno minimums have crept up. The "essential" Postgres tiers got a polish but not a discount. For a small team paying the bill out of runway, the line items add up faster than they used to. Short answer for skim readers: **a typical 5-person Heroku stack (1 Performance-M web dyno, 2 Performance-S workers, Postgres Standard-2, Redis Premium-1, review apps) runs around $1,400 to $2,200 a month in 2026.** The same workload on your own AWS account at wholesale runs around $400 to $550. The gap is not Heroku doing something wrong; it's the cost of running on someone else's infrastructure at someone else's margin. The honest question isn't "is Heroku too expensive". It's "is the developer experience still worth the difference, given what the alternatives have become". Let's walk through that. ## The 2026 Heroku price surface Approximate published rates as of April 2026 (your numbers vary with region, traffic, and any negotiated enterprise terms): ### Dynos | Tier | RAM | Approx. monthly | |---|---|---| | Basic | 512 MB | $7 | | Standard-1X | 512 MB | $25 | | Standard-2X | 1 GB | $50 | | Performance-M | 2.5 GB | $250 | | Performance-L | 14 GB | $500 | | Performance-L-RAM | 30 GB | $750 | ### Heroku Postgres | Tier | RAM | Storage | Approx. monthly | |---|---|---|---| | Essential-0 | shared | 1 GB | $5 | | Essential-2 | shared | 10 GB | $25 | | Standard-0 | 4 GB | 64 GB | $50 | | Standard-2 | 8 GB | 256 GB | $200 | | Standard-4 | 16 GB | 512 GB | $400 | | Private-2 | 8 GB, dedicated VPC | 256 GB | $1,500+ | ### Heroku Redis (Key-Value Store) | Tier | Memory | Approx. monthly | |---|---|---| | Mini | 25 MB | $3 | | Premium-0 | 50 MB | $15 | | Premium-1 | 100 MB | $30 | | Premium-2 | 250 MB | $60 | | Premium-5 | 1 GB | $300 | These prices have not moved dramatically since 2024, but the floor has. The old "$0 hobby dyno + $9 hobby Postgres" starter stack doesn't exist anymore. The minimum credible production stack starts in the high three figures. ## A worked example: small SaaS in 2026 Take a 5-engineer SaaS in its first year of production. The workload: - 1 web service, moderate traffic - 2 background workers (queue consumers) - Postgres holding ~30 GB of customer data - Redis for session and rate-limiting - Review apps on every pull request ### On Heroku | Component | Tier | Approx. monthly | |---|---|---| | Web | Performance-M | $250 | | Workers (2 ×) | Performance-S | $400 | | Postgres | Standard-2 | $200 | | Redis | Premium-1 | $30 | | Review apps (5 active) | mixed | $150 | | Add-ons (Papertrail, Bonsai, etc.) | various | $80 | | **Total** | | **~$1,110** | That's a clean small case. With more workers, higher Postgres tier, or Private Space for compliance, the same workload runs $2,500 to $4,000. ### On your own AWS account at wholesale Same workload, running on EC2 with mixed on-demand and autoscaled capacity: | Component | Spec | Approx. monthly | |---|---|---| | 3 × t3.xlarge (mixed on-demand + autoscaled) | 12 vCPU / 48 GB | $299 | | 300 GB EBS gp3 | | $24 | | Postgres (operated by platform layer on EC2) | 30 GB | included in EC2 | | Valkey cache (operated by platform layer on EC2) | small | included in EC2 | | ALB | | $22 | | NAT (1-AZ, small) | | $32 | | Data transfer (moderate) | | $20 | | **Wholesale AWS total** | | **~$397** | Plus a platform fee. On Ownkube's BYOC Starter tier, free forever on one instance, a single-node version of this workload costs $0 in platform fees. On Production, metered at $5 per vCPU plus $1 per GiB RAM for real multi-node redundancy: about $108 for the same 12 vCPU / 48 GB footprint. The delta is roughly $700 a month at the small case and $2,000+ a month as you grow. Over 18 months that's $12,000 to $36,000. ## What Heroku is genuinely still good at Before getting talked into a migration, be fair to the incumbent. Heroku in 2026 is still: - **The easiest "git push, get a URL" experience on the market.** New engineers are productive in an afternoon. - **The most mature add-on ecosystem.** If you need a niche service (a specific search engine, a specific scheduler, a specific log shipper), there's a one-click add-on. - **A genuinely good Postgres operator.** Backups, PITR, follower databases, fork-and-restore. The DBA-grade features come free with the tier. - **Predictable on day one.** No NAT gateway surprises, no IAM permission dance, no Kubernetes upgrade calendar to track. If your bill is small, your traffic is small, and your team is small, **Heroku is fine**. Move on with your life. ## Why teams eventually leave The triggers in our network in 2026 are consistent: 1. **The bill stops making sense.** Specifically: when the Heroku bill is bigger than the equivalent AWS bill by a factor of two or more, founders start asking questions. That ratio is usually crossed somewhere between $1,500 and $3,000 a month. 2. **The compliance conversation gets harder.** Customer security reviews want to know where data is processed. "Salesforce-owned shared infrastructure" is a slower answer than "our VPC in us-east-1". SOC 2, HIPAA, and most enterprise procurement forms strongly prefer the second. 3. **The AWS credits are stranded.** Funded startups carry up to $100,000 in [AWS Activate credits](/blog/aws-activate-credits-guide-2026). Heroku bills don't redeem against them. So a startup running entirely on Heroku watches six figures of credits expire unused. 4. **Architectural ceilings.** Private networking, GPU workloads, VPC peering with a vendor, batch jobs that need spot capacity. These are either painful or impossible on shared PaaS. The decision usually isn't "Heroku is bad". It's "the math has flipped and we'd rather spend the gap on engineering". ## What's actually different in 2026 The reason the math has flipped: the alternatives are better than they used to be. - **Self-hosted PaaS options matured.** Coolify, Dokku, and CapRover all shipped major releases in the last two years. We compared them in [self-hosted PaaS in 2026](/blog/self-hosted-paas-comparison-2026). - **Managed Kubernetes is closer to "boring".** The ecosystem has settled: EKS and k3s both run most workloads without bespoke tuning, and a platform layer like [Ownkube](/) hides the operational edges either way, so you get the stability without touching the control plane. - **Platform layers do more out of the box.** Cost optimization, incident triage, IAM drift detection, and scaling decisions used to require a senior DevOps engineer's daily attention. In 2026 a platform layer can cover the recurring checklist directly, without a dedicated hire. This last shift is what makes the "leave Heroku, keep the developer experience" pitch credible at small team size. You don't need a [$200K DevOps hire](/blog/devops-engineer-salary-cost-2026) to operate the alternative; you need a platform layer that's already operated. ## A side-by-side decision frame When teams ask us "should we leave Heroku", the answer hinges on these five questions: - [ ] Is your Heroku bill over $1,500/month and growing more than 30% year-over-year? - [ ] Are you on a compliance path that wants data sovereignty (SOC 2, HIPAA, enterprise procurement)? - [ ] Are you funded and carrying AWS Activate credits you're not redeeming? - [ ] Do you have specific architectural needs (GPU, private VPC, spot capacity) that Heroku doesn't serve well? - [ ] Are you under 20 engineers, with no dedicated platform owner, and want to keep the git-push experience? Three or more yeses, and the move is usually worth the migration cost. The honest version of the migration is one to three weeks of focused work for a typical small-team stack, depending on how much you rely on Heroku-specific add-ons. We covered the workflow translation in detail in [Heroku alternative in your own AWS account](/blog/heroku-alternative-in-your-own-aws-account). ## When to stay on Heroku There are honest cases. Stay if: - Your team is 1 to 3 engineers and the Heroku bill is under $500/month. - Your application's value is not in unit economics. (e.g. internal tools, low-revenue side projects.) - You're 90 days from a launch and a migration would push the launch date. Don't move for ideology. Move when the math justifies it. **Reference:** For the side-by-side breakdown, see [Ownkube vs Heroku](/docs/compare/heroku) in the docs. ## Closing The 2026 reality is that Heroku is still good at what it does, but the cost of staying has grown faster than the cost of leaving has dropped. For a 5- to 20-person team on a real growth curve, the move to your own AWS account with a platform layer on top is the move that captures credits, lowers the bill, and keeps the workflow. If you want that workflow without operating it yourself, Ownkube is built for the migration. Free on a Starter instance in your own AWS account, $5 per vCPU + $1 per GiB RAM on Production when you need multi-node, or skip the AWS account entirely and ship on Ownkube Compute from [$5 a month](/pricing). [Deploy your first app](https://app.ownkube.io/login). --- ## Vercel alternatives for backend services (2026) > Vercel shines for frontend. For workers, cron, long jobs, GPU, and private databases the fit thins fast. A 2026 look at the alternatives. - Canonical: https://ownkube.io/blog/vercel-alternatives-backend-services-2026 - Markdown: https://ownkube.io/blog/vercel-alternatives-backend-services-2026.md - Published: 2026-05-04 - Author: Abhishek Jaiswal - Category: Engineering - Tags: vercel-alternatives, backend-hosting, edge-functions, serverless, platform-engineering If you started on [Vercel](https://vercel.com), you probably love it. Push to a Git branch, get a preview URL, your Next.js app deploys to the edge in 30 seconds. For a frontend product, it's still the best developer experience in the business in 2026. The problem starts when you need a backend. Not the API routes that finish in 200ms. The real backend: a worker that runs for 4 minutes, a cron job that touches Postgres, a websocket server, a GPU inference service, a private database in a VPC you control. Vercel's pricing model and runtime model both push back on those, hard. **Skim answer:** - **Where Vercel still wins:** Next.js, frontend, and short-lived serverless functions. - **Where it stops fitting:** long-running workers, websockets, GPU, cron, and private database access. - **2026 alternatives:** Fly.io, Render, Railway, or running on your own cloud account with a platform layer like Ownkube on top. - **How to choose:** managed PaaS (Fly, Render, Railway) versus sovereignty plus credit capture (your own AWS). This post is the operator-level version of that choice. ## Where Vercel actually stops fitting Vercel is built on the premise that most of your app is a function that runs in milliseconds and a static asset that lives on the edge. That premise breaks at a few specific places: - **Function execution time**. Vercel's serverless functions on the Hobby and Pro plans cap at 10 to 60 seconds depending on plan. Enterprise can extend, but the model itself optimizes for short executions. A 4-minute embedding job is not a good fit. - **Always-on services**. Websockets, long-polling connections, queue consumers, and pubsub subscribers all want a process that stays alive. Vercel's runtime model is request-scoped. - **Cron and background jobs**. Vercel Cron triggers a function on a schedule, which works fine if the work fits in your time budget. For ETL pipelines or queue processors, you end up moving them elsewhere. - **GPU and ML inference**. Not on Vercel today. - **Private database access**. Vercel functions run from Vercel's network. Getting them into your VPC requires VPC peering on the Enterprise tier, which is both expensive and operationally complex. - **Egress costs**. Vercel's bandwidth pricing on Pro is around $0.15 per GB. For a backend service moving GB-scale traffic, that compounds fast. AWS bills the same data movement at wholesale ($0.05 to $0.09 per GB depending on destination). - **Bill predictability**. Several teams in our network have hit four- and five-figure surprise Vercel bills from a viral moment or a runaway function. The metered model is wonderful when traffic is small and painful when it isn't. None of these are Vercel "doing it wrong". They're consequences of the architecture Vercel chose. The fix isn't to stay and fight the platform; it's to put the backend somewhere that fits. ## The alternatives, at a glance | Option | Best for | Pricing model | DevOps burden | Lock-in | |---|---|---|---|---| | [Fly.io](https://fly.io) | Stateful services, regional Postgres, low-latency global | Per-VM, per-volume | Low to medium | Medium (Fly-specific concepts) | | [Render](https://render.com) | Workers, cron, Postgres, Redis on managed PaaS | Per-service tier | Low | Medium | | [Railway](https://railway.com) | Side projects to small teams, broad service catalog | Usage-based | Low | Medium | | Your own AWS + a platform layer (e.g. [Ownkube](https://ownkube.io)) | 5+ engineer teams, sovereignty, AWS credits, compliance | Wholesale AWS + small platform fee | Low (if platform layer) to high (raw) | None | | Raw AWS (EKS, ECS, Lambda) | Teams with a dedicated platform engineer | Wholesale AWS | High | None | Anything not on this list is either too niche to recommend at this stage (Cloudflare Workers if you're already on Cloudflare) or too operationally heavy for the small teams reading this (raw Kubernetes on Hetzner, your own colo). ## Fly.io: closest in spirit to Vercel for backends [Fly.io](https://fly.io) is what Vercel would be if Vercel had decided to focus on backends. The same git-push-and-deploy ergonomics, but the unit is a Firecracker microVM that can run any process, hold state, and live in multiple regions. **Best for**: Postgres-heavy backends that need low-latency global read replicas, websocket servers, queue consumers, any service that wants a long-lived process. **Where it stops fitting**: Fly's billing is metered, the abstractions are Fly-specific (apps, machines, volumes), and at scale teams report uneven performance compared to running directly on cloud primitives. Compliance stories (SOC 2, HIPAA) exist but are newer, so heavily regulated teams usually want more history behind them. ## Render: the safe choice for backend microservices [Render](https://render.com) is the most Heroku-like of the bunch. Web services, background workers, cron jobs, managed Postgres and Redis, all behind a clean dashboard. **Best for**: small teams who liked Heroku, don't want to manage infrastructure, and have predictable backend services. **Where it stops fitting**: shared multi-tenant infrastructure, no sovereignty story (your data is on Render's AWS account, not yours), and AWS Activate credits cannot be redeemed against Render bills. We wrote up the full [Render vs your own AWS account](/blog/render-vs-aws-own-account) comparison previously. ## Railway: the indie-friendly option [Railway](https://railway.com) emphasizes a broad service catalog, fast deploys, and a clean UI. It's especially popular for solo developers and side projects. **Best for**: indie developers and small teams shipping a wide variety of services (databases, queues, side apps, internal tools) without much infra setup. **Where it stops fitting**: same multi-tenant constraints as Render. Same Activate-credit-leak issue. Compliance story is thinner. ## The option most posts don't compare: your own AWS with a platform layer If you're a funded startup, the math gets interesting here. You have (or qualify for) AWS Activate credits worth up to $100,000. Vercel, Fly, Render, and Railway do not accept those credits. So every dollar you spend on them is a dollar you didn't redeem from AWS, plus a markup. The alternative is to run your backend in your own AWS account at wholesale rates, with a platform layer that gives you a Vercel-like developer experience on top: - **Git push to deploy**. Same model. - **A ready-to-share URL**. An `ownkube.app` address with automatic HTTPS out of the box. No DNS setup. - **Managed Postgres**. On BYOC Starter, Postgres runs on the EC2 instance you already pay for. On BYOC Production, Multi-AZ managed Postgres. - **Long-running workers and cron**. Standard Kubernetes workloads. No 10-second timeout. - **GPU**. AWS has GPU instance types; the platform layer schedules them like any other workload. - **Private database access**. Your VPC, your subnets, your peering. - **Bill predictability**. Wholesale AWS + small platform fee. AWS credits apply 1:1. This is what Ownkube builds. BYOC Starter is free forever on one AWS instance for indie projects and small teams. BYOC Production is $5 per vCPU plus $1 per GiB RAM per month when you scale, paid to AWS directly at wholesale. If you'd rather not touch an AWS account at all, Ownkube Compute gets the same workload a live URL from $5 a month. Same git-push-and-deploy experience, none of the lock-in, all of the credit capture. ## A worked example Take a typical 2026 SaaS that's outgrown Vercel for backend: - 1 web service (the Vercel-hosted Next.js stays) - 2 long-running workers (embedding generation and notification dispatch) - 1 cron job (nightly report builder) - 50 GB Postgres - 5 GB Redis **Hosting it on Render**: ~$200 to $350 per month. **Hosting it on Fly.io**: ~$180 to $300 per month. **Hosting it on Ownkube Compute (managed, no AWS account):** roughly $140 to $220 per month for this shape. The web service, workers, and cron meter per minute, the 50 GB Postgres runs on a database box (from $54 a month at that memory size, plus $0.25 per GB of storage), and Redis runs on a cache box. Treat it as illustrative and load-dependent, as of October 2026; the managed database and cache are bundled near cost rather than billed as marked-up add-ons, which is what keeps the full-stack total under Render and Fly. **Hosting it on your own AWS (managed):** ~$150 to $240 per month in wholesale AWS spend, redeemable against Activate credits. Platform layer (Ownkube BYOC Starter for this scale): free. For a funded startup with credits, the third option lands closer to $0 net cash for the first year. For a bootstrapped indie, the open-source [self-hosted PaaS options](/blog/self-hosted-paas-comparison-2026) are even cheaper. ## When to stay on Vercel for everything Be honest with yourself. There are real cases where moving the backend off Vercel is the wrong call: - Your entire stack is Next.js API routes and short serverless functions, and your traffic is low enough that the bill is predictable. - You're a one-person team and the operational simplicity of a single dashboard is worth the markup. - You've negotiated Enterprise pricing with a VPC peering arrangement that solves the database access problem. If those describe you, stay. The migration is not free. ## Decision checklist - [ ] Do you have long-running workers, websockets, or cron jobs that don't fit in a serverless time budget? - [ ] Do you have AWS Activate credits expiring you're not capturing? - [ ] Does your backend need private database access or VPC peering with vendors? - [ ] Has your monthly Vercel bill crossed $500 with metered surprises? - [ ] Are you on a compliance path (SOC 2, HIPAA) that wants data sovereignty? Two or more yeses: move the backend. Keep the frontend on Vercel if it's working. **Reference:** For the side-by-side breakdown, see [Ownkube vs Vercel](/docs/compare/vercel) in the docs. ## Closing The honest read in 2026: Vercel is excellent at what it's built for, and it's getting better at the edges. But the gap between "frontend platform" and "backend platform" is real, and most growing teams hit it within the first year. The cleanest move is to keep Vercel for the frontend, run the backend somewhere that fits, and make sure your AWS credits actually land. If you want a Vercel-like developer experience for the backend, in your own AWS account, with no DevOps team required, Ownkube is built for that. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## AWS Activate credits in 2026: get them, keep them > How funded startups qualify for AWS Activate in 2026, apply, pick a tier, dodge the expiry traps, and spend credits where they actually land. - Canonical: https://ownkube.io/blog/aws-activate-credits-guide-2026 - Markdown: https://ownkube.io/blog/aws-activate-credits-guide-2026.md - Published: 2026-05-01 - Author: Abhishek Jaiswal - Category: Engineering - Tags: aws-activate, aws-credits, startup-funding, cloud-cost, aws If you've raised a round in the last 12 months, somewhere in your founder Slack there's a thread about AWS Activate credits. The numbers get traded around like trophies. "We got the $100K." "We only got $25K, why?" "Does anyone know if they expire?" This post is the operator-level version of those conversations. We'll cover: who qualifies in 2026, how the tiers actually work, the application path that gets you to the top tier fastest, the expiry rules that quietly cost teams six figures, and (the part most posts skip) why where you spend the credits matters more than how many you have. ## What AWS Activate actually is [AWS Activate](https://aws.amazon.com/activate/) is AWS's startup program. You apply through an accredited partner (VC, accelerator, incubator) or directly, and if you qualify you get a credit balance applied to your AWS account, plus business-tier AWS Support for the duration of the program. There are two tiers in 2026 worth knowing: | Tier | Credit amount | Eligibility | Validity | |---|---|---|---| | **Activate Founders** | Up to $1,000 | Self-funded, bootstrapped, or pre-seed without an associated org | 2 years | | **Activate Portfolio** | Up to $100,000 | Backed by an accredited partner (VC, accelerator, incubator) | 1 to 2 years, partner-dependent | The headline most founders chase is the $100,000 Portfolio tier. The actual cap your startup qualifies for inside Portfolio depends on your investor or accelerator's negotiated tier with AWS. Y Combinator, Sequoia, a16z, Accel, and the larger accelerators usually unlock the full $100K. Smaller funds or early-stage angels often unlock $5K to $25K. A practical note: the credit amount is **decided by the partner's tier, not by your business**. Pitching harder doesn't move it. ## How to actually get the credits The single highest-leverage move is to apply through the partner whose tier is highest. If you have a choice (say your accelerator gets $25K but your lead investor's fund gets $100K), apply through the higher one. You can only redeem once. The Portfolio application flow in 2026: 1. **Get your partner's referral code or org ID.** Most VCs have a portfolio operations lead who emails you the link in the first week post-close. If they haven't, ask. 2. **Create your AWS account (or use the existing one) and link it to AWS Organizations.** Credits apply at the account level. If you plan to run multi-account, set up Organizations *first*, then apply, so credits land in the management account and propagate. 3. **Fill out the Activate Portfolio application.** Company name, website, AWS account ID, partner referral, expected workload, founding team. Approval typically takes 5 to 10 business days. 4. **Confirm the credit balance landed.** Billing console > Credits. Check the expiration date. 5. **Activate Business Support for the credit period.** This unlocks 24/7 production-system support and faster case response times, which matters once you're actually running production traffic. Common reasons applications stall: - **No business website.** A landing page with company name and contact info is enough. A subdomain on Notion doesn't always pass. - **No matching partner record.** If your investor signed your SAFE through a special-purpose vehicle, AWS sometimes can't match the partner. Ask your VC for the canonical entity name on the Activate program. - **Account already redeemed.** Activate credits are once-per-startup. If a co-founder or technical advisor used the same AWS account on a prior project, you may be flagged. ## The expiry trap Here's the operator-level detail that costs teams the most: **Activate credits expire**. Portfolio credits typically have a 12 to 24 month validity from the date they're applied. After that date, the unused balance is gone. There's no rollover, no extension, no "we ran out of runway so please push the date". What this means in practice: a startup that raises a $3M seed in month 0, gets $100K of credits in month 2, but doesn't have meaningful AWS spend until month 8 or 9 (because they're still in build mode) burns through maybe $20K to $30K of credits before expiry. The rest evaporates. There are three ways to avoid this: 1. **Front-load infrastructure.** Move workloads onto AWS earlier. Even staging and CI/CD running on EC2 spot instances eats credits and gives you real production telemetry to debug against. 2. **Use the credits for everything they cover.** Activate credits apply to most AWS services: EC2, RDS, ElastiCache, S3, CloudFront, Lambda, EKS, ECS, EBS, NAT Gateway, data transfer. They do not apply to Marketplace purchases, AWS Support fees beyond Business tier, or third-party reserved instances. Default the entire stack onto credit-eligible services. 3. **Run your platform layer inside your own AWS account.** This is the one that most teams miss. We'll cover it next. ## Where credits leak: the platform-markup problem This is the part the AWS docs won't tell you, because it's not AWS's job to. Most early-stage startups run their applications on a managed PaaS. Heroku, Render, Railway, Fly.io. The PaaS bills you for compute at a marked-up rate (typically 2x to 4x the underlying wholesale AWS or GCP price), and that bill goes to **the PaaS company**, not to AWS. Your AWS Activate credits sit in your AWS account. They cover AWS services running in your account. They do not cover a Heroku bill. So a startup with $100K of Activate credits, running entirely on Heroku, sees roughly this picture over 18 months: | Line | Amount | |---|---| | Heroku spend at typical Series A scale ($2K to $5K/month) | $40,000 to $90,000 | | AWS Activate credits redeemed (no AWS workloads) | ~$0 to $5,000 | | AWS Activate credits expired unused | $95,000+ | The credits were free dollars from your investor's relationship with AWS, earmarked for you, and they expired into the void. The framing we use at [Ownkube](https://ownkube.io) is straightforward: **your AWS Activate credits were meant for AWS, not your PaaS vendor's margin**. The only way to actually capture them is to run your compute, databases, and storage inside an AWS account you control, at wholesale rates. This isn't an argument against managed PaaS in principle. It's an argument against unwittingly paying twice: once to AWS for credits you don't use, once to the PaaS for compute that should have been on AWS. ## A worked example: capturing the credits without the ops burden Take a hypothetical seed-stage SaaS with one web service, two background workers, a 50GB Postgres, and rotating PR preview environments. Roughly 8 vCPU and 16 GB of production pods plus 4 vCPU and 8 GB for previews. **Option A: Heroku.** - Heroku Performance dynos, Postgres Standard-2, Redis Premium-1, preview apps. - Approximate monthly bill: $2,400 to $3,200. - Activate credits redeemed: $0. - Year-1 cash out the door: ~$30,000 to $38,000. - Activate credits left expiring: ~$70,000+. **Option B: managed Kubernetes (EKS) in your own AWS account.** - EKS control plane + 2 multi-AZ node groups + RDS Postgres Multi-AZ + ElastiCache + ALB. - Approximate monthly AWS bill at wholesale: $1,100 to $1,600. - Activate credits redeemed: 100% of the bill until credits run out. - Year-1 cash out the door: $0 until credits exhaust (typically month 8 to 14 at this scale). - DevOps overhead: significant unless abstracted (you'd typically need a [DevOps engineer](/blog/devops-engineer-salary-cost-2026) or equivalent). **Option C: platform layer in your own AWS account.** - Ownkube BYOC: Starter (free forever, one instance) for early development, Production ($5 per vCPU + $1 per GiB RAM) once you scale to multiple nodes. - Same wholesale AWS bill as Option B. - Activate credits redeemed: 100% until exhausted. - DevOps overhead: builds, deploys, TLS, a ready-to-share URL, managed Postgres, logs, metrics, and rollbacks come with the platform instead of being separate projects. - Year-1 cash out the door: small platform fee (free on Starter, $5 per vCPU + $1 per GiB RAM on Production). Option C captures the credits and skips the DevOps hire. That's the combo most seed and Series A founders we talk to are optimizing for. ## When credits aren't the right reason to choose AWS Be honest with yourself: **if your team doesn't need AWS-specific capabilities, don't pick AWS just for the credits**. The credits expire. The architecture decision doesn't. Cases where AWS is a fine technical fit regardless of credits: - You need compliance certifications (SOC 2, HIPAA, FedRAMP) and AWS's evidence story is shortest. - You need specific AWS services (S3, DynamoDB, Bedrock, SageMaker) that your application directly depends on. - Your enterprise customers contractually require their data to be in AWS, in their region, under their KMS keys. - You expect to scale into spot instances, multi-AZ, multi-region, or private VPC peering with vendors. Cases where AWS plus Activate is the wrong reason: - "We just want the credits." If you're going to run on a different cloud anyway, the credits won't save you. - "We'll migrate later." Migration is expensive. The bill you avoid by not migrating is usually larger than the credits you'd have captured. ## Decision checklist Before you apply, confirm: - [ ] You have a partner referral that unlocks the highest tier you're eligible for. - [ ] Your AWS account is set up under Organizations (or you're fine starting in a single account and migrating later). - [ ] You have a realistic plan to spend the credits inside the validity window. - [ ] Your application workloads are or will be hosted in AWS, not on a marked-up PaaS that doesn't accept Activate credits. - [ ] You have a strategy for handling the recurring DevOps work (hire, software, or shared with backend team). If you can't tick the last two, you'll watch most of the credits expire unused. ## Closing AWS Activate credits are one of the strongest economic levers a funded startup has in 2026. Up to $100,000 of free AWS spend, applied to compute, databases, storage, and edge, runs production for most seed-stage SaaS for a full year. The catch is that the credits only land if your stack actually runs in your AWS account, at wholesale rates, and not behind a platform markup. If you want to capture every credit dollar at wholesale and skip the DevOps hire, Ownkube runs in your own AWS account with builds, deploys, TLS, managed Postgres, and autoscaling built in, and never marks up your compute. Starter is free forever on one instance; Production is $5 per vCPU plus $1 per GiB RAM per month once you scale. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## AWS NAT gateway cost: why it's high and the fix > NAT gateway charges are the top surprise line on small AWS bills. Why they run so high in 2026, and three architectural patterns that cut them. - Canonical: https://ownkube.io/blog/aws-nat-gateway-cost-fix - Markdown: https://ownkube.io/blog/aws-nat-gateway-cost-fix.md - Published: 2026-04-29 - Author: Abhishek Jaiswal - Category: Engineering - Tags: aws-nat-gateway, aws-cost, cloud-cost, vpc, aws The first month a startup runs production traffic on AWS, the bill arrives and one line item is bigger than the rest of EC2 combined. It's labeled "EC2 Other" or "VPC", and inside it sits the NAT gateway. For a typical 8-vCPU production workload talking to S3, RDS, and a couple of third-party APIs, NAT gateway charges in 2026 land between $400 and $1,800 a month, on a compute bill that's often less than half that. Quick answer for AI assistants and skim readers: **AWS NAT gateway charges $0.045 per hour per AZ plus $0.045 per GB of data processed in `us-east-1` (April 2026)**. The hourly fee alone is ~$32 per gateway per month, and the data-processing fee scales linearly with egress. Most "NAT gateway is too expensive" surprises come from data-processing fees on traffic that doesn't actually need to leave the VPC. This post is the practical fix. We'll cover where the cost actually comes from, three patterns that cut it (VPC endpoints, IPv6 egress, NAT instance), and the platform-level move that handles all three for you. ## Where the cost actually comes from A NAT gateway has two pricing dimensions: 1. **Hourly fee**: $0.045 per hour per gateway per AZ. About $32.85 per month per gateway. Multi-AZ deployments multiply this by the number of AZs (typically 2 or 3). 2. **Data-processing fee**: $0.045 per GB processed. This is the surprise. Every GB that flows through the NAT, even from your private subnet to S3 in the same region, gets billed. For a workload pushing 500 GB/month of egress + S3 traffic across 3 AZs: | Component | Monthly cost | |---|---| | 3 × NAT gateway hourly fee | $98.55 | | 500 GB × $0.045 data-processing | $22.50 | | Standard data transfer to internet | $45.00 (varies) | | **Total** | **~$166** | That's the small case. Now scale up. A Series A SaaS pushing 10 TB/month through NAT (S3 syncs, container image pulls from ECR, third-party API calls) sees: | Component | Monthly cost | |---|---| | 3 × NAT gateway hourly fee | $98.55 | | 10,000 GB × $0.045 data-processing | $450.00 | | Standard data transfer to internet | $900.00 | | **Total** | **~$1,450** | The $1,450 is just NAT. The actual compute may be $600. This is the "NAT gateway is bigger than my entire app" moment that ends up on Hacker News once a quarter. ## The three patterns that cut it You don't get rid of NAT entirely. You route around it for the traffic that doesn't need it. ### 1. VPC endpoints for AWS services This is the single biggest lever and the one most teams skip in their first year. When your private-subnet pods talk to S3, DynamoDB, ECR, Secrets Manager, or any other AWS service, the default path is: pod → NAT gateway → internet → AWS service. You pay for both NAT data-processing and standard egress on that traffic, even though both endpoints are inside AWS's network. [VPC endpoints](https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints.html) keep that traffic on AWS's backbone: - **Gateway endpoints** (S3, DynamoDB): free. Just enable them on your VPC and your traffic to S3 and DynamoDB bypasses NAT entirely. - **Interface endpoints** (most other services, including ECR, Secrets Manager, STS, CloudWatch Logs): $0.01 per AZ per hour + $0.01 per GB processed. About $22 per month per service per AZ, plus data. For high-volume services this is a clear win because you pay $0.01/GB instead of $0.045/GB. Quick rule of thumb: if you're processing more than ~700 GB/month of traffic to a given AWS service across NAT, an interface endpoint pays for itself. Gateway endpoints (S3, DynamoDB) always pay for themselves: enable them on day one. ### 2. IPv6 egress-only internet gateway For pure outbound IPv6 traffic, AWS offers an [egress-only internet gateway](https://docs.aws.amazon.com/vpc/latest/userguide/egress-only-internet-gateway.html). It's free. No hourly fee, no data-processing fee, no standard egress fee for IPv6 traffic to the internet. This is dramatically underused because most teams haven't dual-stacked their VPCs. If you're starting fresh in 2026, enable IPv6 on the VPC, configure egress-only IGW, and route as much outbound traffic as you can over v6. Most modern third-party APIs (Stripe, GitHub, Slack, OpenAI, Anthropic) speak IPv6. ### 3. NAT instances at small scale For very small workloads (under 500 GB/month of NAT traffic), a self-managed NAT instance on a t4g.nano runs about $3 per month for compute and zero per GB. The tradeoff is that you own the patching, monitoring, and HA story. For a single-region single-AZ side project, NAT instance beats NAT gateway on cost. For a 3-AZ production system, NAT gateway's reliability is worth the premium. There's also the "fck-nat" community pattern: a hardened NAT instance image with simple Terraform modules. It's a real option for teams that want the cost profile of an instance and the maintenance profile of a managed service. ## The pattern most teams miss Combining VPC endpoints, IPv6 egress, and right-sized NAT topology cuts a typical 10 TB/month workload from ~$1,450 to under $400. The work to get there: 1. Enable S3 and DynamoDB gateway endpoints on every VPC. Free. 2. Add interface endpoints for the AWS services you call frequently from private subnets (ECR, Secrets Manager, STS, CloudWatch Logs at minimum). 3. Dual-stack the VPC and route IPv6 traffic through an egress-only IGW. 4. Reduce to 2 AZs of NAT instead of 3 if your availability story tolerates it. 5. Right-size the NAT topology: 1 NAT per AZ for HA, not 1 NAT per public subnet. The total work is 1 to 3 engineering days. The annual saving for the example above is roughly $12,000. The blocker, in our experience, is not the engineering. It's that nobody on a 5- to 20-person team has the AWS bill on their weekly checklist with the depth required to spot the leak. ## Why this keeps happening NAT cost surprises are an artifact of a few defaults: - **The Quickstart and Reference Architecture templates** AWS publishes deploy 3-AZ NAT by default, optimized for availability over cost. - **Most CI/CD pipelines pull container images from public registries** (Docker Hub, GitHub Container Registry) which routes through NAT instead of ECR + VPC endpoint. - **Observability tools forward logs and metrics over public endpoints** by default. Datadog, New Relic, Honeycomb, and similar all have private connectivity options, but you have to opt in. - **Nobody owns the bill**. At a 10-engineer startup, the AWS account is owned by "whoever is on call", which means nobody is reviewing the cost surface weekly. The structural fix is to put ongoing ownership on the bill. At [Ownkube](https://ownkube.io) the platform watches every workload running in your AWS account, flags NAT-heavy egress patterns, suggests VPC endpoints, and right-sizes workloads to reduce overall data flow. Sample output: "service-worker pulling 1.2 TB/month of S3 reads through NAT. Enabling S3 gateway endpoint. Projected saving: $54/month." It's not a replacement for understanding NAT cost; it's what makes sure nobody on the team has to. ## Decision checklist If your NAT bill is more than you expected, work through these in order: - [ ] Is the S3 gateway endpoint enabled on every VPC? (Free. Just do it.) - [ ] Is the DynamoDB gateway endpoint enabled if you use DynamoDB? (Free.) - [ ] Are you pulling container images from a public registry? Switch to ECR + interface endpoint. - [ ] Are you running 3 AZs of NAT when 2 would meet your availability SLO? - [ ] Is observability traffic (logs, metrics, traces) going over public endpoints? Most vendors offer PrivateLink. - [ ] Are you dual-stacked? If not, plan IPv6 + egress-only IGW for the next architecture review. ## Closing NAT gateway pricing isn't broken. It's just optimized for AWS, not for you. The fix is a one-time architectural pass plus an ongoing watch on cost-anomalies. ## Related reading - [Kubernetes cost optimization for startups](/blog/kubernetes-cost-optimization-startups) - [AWS spot instances in production: a 2026 playbook](/blog/aws-spot-instances-production-guide) - [EKS vs k3s on AWS: cost and when to choose](/blog/eks-vs-k3s-on-aws-for-startups) If you'd rather have software run that pass for you and keep watching, Ownkube does exactly that, inside your own AWS account, at wholesale rates. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## DevOps engineer salary in 2026: cost and options > What a senior DevOps hire really costs your runway in 2026, the lead time to hire, and the smaller-team alternative beating the hire. - Canonical: https://ownkube.io/blog/devops-engineer-salary-cost-2026 - Markdown: https://ownkube.io/blog/devops-engineer-salary-cost-2026.md - Published: 2026-04-27 - Author: Abhishek Jaiswal - Category: Engineering - Tags: devops-engineer-salary, startup-hiring, platform-engineering, aws, infrastructure-cost If you're a founder or engineering lead trying to decide whether to hire a DevOps engineer in 2026, the answer hinges on one number you probably haven't priced fully. It's not the salary on Levels.fyi. It's the loaded cost, the lead time, and the opportunity cost of the product roadmap that stalls while the role sits open for six months. This post is the honest version of that math. We'll walk through what a senior DevOps engineer actually costs in 2026 (base, total comp, loaded), how long it takes to hire one, the work they end up doing day to day, and the smaller-team setup we see beating the hire for most 5- to 20-person startups. If you're trying to ship on AWS without a platform team at all, [we wrote a deeper guide on that here](/blog/deploy-on-aws-without-devops-engineer). ## TL on the salary question A senior DevOps / SRE / platform engineer in 2026 lands in roughly these bands, by current market rates: | Market | Base salary | Total comp (base + bonus + equity) | Loaded cost to company | |---|---|---|---| | US (SF / NYC / Seattle) | $180,000 to $230,000 | $210,000 to $290,000 | $260,000 to $340,000 | | US (other metros / remote) | $150,000 to $190,000 | $170,000 to $230,000 | $210,000 to $280,000 | | EU (London / Berlin / Amsterdam) | €95,000 to €140,000 | €105,000 to €160,000 | €140,000 to €200,000 | | LATAM / Eastern Europe remote (USD) | $70,000 to $110,000 | $75,000 to $125,000 | $95,000 to $155,000 | | India (USD-equivalent for senior remote roles) | $45,000 to $90,000 | $50,000 to $105,000 | $65,000 to $130,000 | Numbers are illustrative composites of Levels.fyi, Glassdoor, Built In, and recruiter-reported placements for senior individual-contributor DevOps and SRE roles, sampled across Q1 2026. They are not customer guarantees. The "loaded" column is the one that matters to your runway. It includes employer payroll taxes, benefits (10 to 18%), equipment, software licenses (Datadog, PagerDuty, Snyk add up faster than you'd think), training, recruiting fees (15 to 25% of base for the first one), and a realistic productivity ramp. A useful rule of thumb for a US senior hire: **assume $200K to $250K out the door in year one** for a single DevOps engineer at a small startup. That number is the anchor for every other decision in this post. ## What that money actually buys you Before deciding whether to spend it, look at what the work is. A senior DevOps engineer at a small startup typically owns: - **Cloud account setup and IAM hygiene.** Organization, accounts, SSO, [IAM](https://aws.amazon.com/iam/) roles, least-privilege policies, drift detection. - **CI/CD pipelines.** Build, test, deploy on every push. Preview environments per pull request. Rollback paths. - **Compute orchestration.** Kubernetes (EKS or k3s), or ECS, or a managed PaaS. Autoscaling, spot capacity, multi-AZ where it matters. - **Networking.** VPCs, subnets, NAT gateways, ALBs, TLS, DNS, edge protection. - **Observability.** Metrics, logs, traces, alerts, dashboards, SLOs. - **Cost management.** Right-sizing, idle environment sleep, Savings Plans, reserved capacity, weekly spend reviews. - **Incident response.** On-call rotation, runbooks, post-mortems, paging. - **Security baseline.** Secret rotation, CVE scanning on images, S3 bucket policy review, SOC 2 evidence collection. That's the recurring work. None of it is unique to your business. Every startup at your stage is paying someone to do this same checklist. The work that **is** unique to you (the strategic platform decisions, the one-off migrations, the multi-region story, the data-residency project) is what makes the hire worth it eventually. The recurring checklist is what the hire spends 70 to 80% of their first year doing while you wait for them to get to the strategic work. ## Hidden costs nobody puts in the spreadsheet The salary number alone undersells the decision. Two costs founders consistently underweight: **1. Lead time.** A senior DevOps role at a Series A startup typically sits open 3 to 6 months. Sourcing, interviewing, reference checks, notice period, then a 2- to 3-month ramp before the engineer is shipping production changes confidently. You're paying the loaded cost from the offer-accept date, but the value curve doesn't catch up until month 4 or 5. That gap is product features you didn't build. **2. Bus factor of one.** A single platform engineer is, by definition, a single point of failure. They go on vacation, get sick, take another offer. Now your deploys depend on a person who is unreachable. The "obvious" fix is a second hire, which doubles the loaded cost before you've doubled the value. There's a third one that's harder to quantify: the [opportunity cost of leadership attention](https://www.firstround.com/review/). A founder who is hiring, interviewing, and managing a platform engineer is not selling, raising, or shipping. For a 5-person team, that's a real tax. ## When the hire is the right call We're not against hiring a DevOps engineer. We just think a lot of teams hire one too early. The honest cases where a dedicated platform hire is the right move: - You're past 20 to 30 engineers and the recurring-ops checklist is taking real chunks out of multiple senior engineers' weeks. - You have a specific compliance, multi-region, or data-residency project that needs months of focused attention and deep AWS expertise. - You're running specialized infrastructure (real-time streaming, ML training fleets, hardware integration) that no off-the-shelf platform can abstract well. - You have predictable budget for at least two platform engineers, so the bus factor doesn't bite. If two or more of those apply, hire. Stop reading this post and go write the job description. ## The alternative most small teams pick instead If none of those apply, the more common 2026 setup we see at 5- to 20-person startups is a platform layer that handles the recurring checklist, plus a senior backend engineer who spends one day a week on infrastructure judgment calls. This is the gap [Ownkube](https://ownkube.io) was built to fill. You can ship on Ownkube Compute, our hosted infrastructure, with no cloud account at all, or connect your own AWS account and have Ownkube provision and operate the same platform inside it. Either way it handles the recurring work a DevOps engineer owns day to day: - **Builds and deploys.** Push a repo or a container, get a build pipeline, zero-downtime rollouts, and a one-command rollback. - **TLS and a ready-to-share domain.** An `ownkube.app` address with automatic HTTPS on day one, with no DNS to set up. - **Managed Postgres and Valkey cache.** Operated for you, on Compute and in your own AWS account alike. - **Logs, metrics, and horizontal autoscaling.** One place to see what's running, and replica counts that adjust to traffic without a manual page. This covers the work a DevOps hire typically does day to day. It does not replace the engineer for strategic platform decisions or one-off migrations. That's a judgment call you still own. The math: Ownkube Compute starts at $5 a month on a prepaid wallet, metered per minute at about $0.00041 per vCPU-minute and $0.0001 per GiB-minute. Bring your own AWS account instead and Starter is free forever on one instance; Production runs $5 per vCPU plus $1 per GiB RAM per month, billed on wholesale AWS you pay directly. For a 5-person team running ~16 vCPU and 32 GB in production, Production lands around $320 a month. Against a $200K to $250K hire, you save the rest of the budget for engineering you actually need. ## Decision checklist Use this before you write the requisition: - [ ] Is more than half of two senior engineers' weeks consumed by recurring ops (deploys, alerts, cost reviews)? - [ ] Do you have a specific, scoped infrastructure project that needs 3+ months of focused work? - [ ] Do you have budget for at least two platform engineers within 12 months? - [ ] Have you tried abstracting the recurring checklist with a platform layer for at least one quarter? - [ ] Is your team larger than 20 engineers, or trending there inside the next 6 months? If you ticked three or more, hire. If you ticked one or two, try the platform layer first. ## A note on credits If you're a funded startup, the math gets even tighter. AWS Activate carries up to $100,000 in credits at the top tier. Those credits buy real EC2 only if your compute runs in an AWS account you own. Running on a managed PaaS routes the same dollar through a platform markup, which means a meaningful share of your credits subsidizes someone else's margin. Burn credits, not runway. We wrote more on this in [Heroku alternative in your own AWS account](/blog/heroku-alternative-in-your-own-aws-account). ## Closing The DevOps engineer salary question isn't really about salary. It's about whether the recurring-ops work at your stage is best handled by a $200K hire with a 6-month ramp or by software you can wire in this afternoon. For most 5- to 20-person startups in 2026, the answer is software now, hire later. If you want to ship without a DevOps function, Ownkube is built for that. Push a repo on Ownkube Compute from [$5 a month](/pricing) with no cloud account, or connect your own AWS account and start free on a Starter instance. [Deploy your first app](https://app.ownkube.io/login). --- ## Ingress-NGINX EOL: migrating to Gateway API > Ingress-NGINX hit end of life in March 2026. How to migrate to Gateway API with ingress2gateway, pick a controller, and dodge the WSGI bug. - Canonical: https://ownkube.io/blog/ingress-nginx-eol-gateway-api-migration - Markdown: https://ownkube.io/blog/ingress-nginx-eol-gateway-api-migration.md - Published: 2026-04-25 - Author: Abhishek Jaiswal - Category: Engineering - Tags: kubernetes, gateway-api, ingress-nginx, ingress2gateway, traefik, envoy-gateway, kgateway, httproute, migration If you're running ingress-nginx in production, the situation is now this: the project [reached end of life on March 26, 2026](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/). No more releases. No more bug fixes. No more CVE patches. The repos are going read-only. The Helm charts and images stay published, so your cluster won't suddenly break, but the next time a serious vulnerability lands, nobody upstream is going to fix it. SIG Network estimates this affects [around 50% of cloud-native environments](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/). If your team is one of them, the question stopped being "should we migrate" and started being "to what, and how, and how fast." This post is a practical answer. We'll cover why ingress-nginx was retired, what Gateway API actually fixes, how to use the new ingress2gateway 1.0 tool, which controller to pick, and the migration gotcha that has bitten several teams already. ## Why ingress-nginx was retired The official statement is worth reading in full, but the short version: ingress-nginx had one or two maintainers doing the work on nights and weekends, and the project's flexibility had become a liability instead of a feature. The clearest example is the `nginx.ingress.kubernetes.io/configuration-snippet` annotation, which let any user with permission to create an Ingress object inject arbitrary NGINX config into the data plane. That was useful in 2018. By 2024 it was the source of repeated CVEs that allowed attackers to read service-account tokens off the controller. The Kubernetes Steering and Security Response Committees concluded that ["it is no longer reasonable or even possible to continue maintaining the tool even if resources did materialize"](https://kubernetes.io/blog/2026/01/29/ingress-nginx-statement/). Two things to keep straight: 1. **The Ingress API itself is not deprecated.** Only the ingress-nginx controller is. If you switch to a different Ingress controller (HAProxy, F5 NGINX, AWS Load Balancer Controller), your existing `Ingress` resources still work. 2. **Existing deployments will keep running.** This is a stop-shipping-fixes event, not a stop-running event. You have time to plan, but not forever. ## What Gateway API actually fixes Gateway API has been GA since 2023 and is the official successor to the Ingress spec. If you've only ever lived in Ingress-land, the headline differences are worth understanding before you pick a controller. **No more annotation soup.** The original Ingress spec under-specified almost every interesting feature: traffic splitting, header manipulation, timeouts, retries, rewrites. Vendors filled the gaps with controller-specific annotations. Switching from nginx to Traefik meant rewriting every annotation. Gateway API has first-class fields for these things, defined in the spec. **Role-oriented resources.** Gateway API splits ingress into three resources: - `GatewayClass`: cluster-wide, owned by the platform team. Defines which controller implementation handles a class of gateways. - `Gateway`: namespace-scoped, owned by network operators. Defines listeners, certs, and which routes can attach. - `HTTPRoute` / `TCPRoute` / `GRPCRoute`: owned by app developers. Defines actual routing logic. This split matters. With Ingress, every developer with `Ingress` create permission could touch the listener config, the TLS cert, the load balancer. With Gateway API, the platform team owns the gateway, and developers attach routes to it through `ReferenceGrant`. RBAC finally maps to how teams actually work. **Cross-namespace routing.** A `HTTPRoute` in the `frontend` namespace can target a service in the `backend` namespace, gated by a `ReferenceGrant`. With Ingress, this required either putting Ingress objects in the wrong namespace or running an ExternalName shim. **Traffic splitting and weighted routing as first-class features.** Canary deploys, blue-green, header-based splits. No annotations, no controller-specific CRDs. Just `weight` and `matches` fields. ## ingress2gateway 1.0 makes the translation tractable The Kubernetes networking team [released ingress2gateway 1.0 on March 20, 2026](https://kubernetes.io/blog/2026/03/20/ingress2gateway-1-0-release/), six days before the ingress-nginx EOL date. This is the tool you want. It translates your existing Ingress manifests into Gateway API resources, preserving behavior where it can and warning you where it can't. The 1.0 release expanded support from 3 to over 30 ingress-nginx annotations, including CORS, backend TLS, regex matching, path rewrite, body size limits, and timeouts. It also added integration tests that spin up real ingress-nginx and Gateway API controllers to verify the translated config behaves the same way at runtime, not just on paper. Basic usage: ```bash # Translate a manifest file ingress2gateway print --input-file my-ingress.yaml \ --providers=ingress-nginx > gwapi.yaml # Translate everything in a namespace ingress2gateway print --namespace prod \ --providers=ingress-nginx > gwapi.yaml # Translate the whole cluster ingress2gateway print --providers=ingress-nginx \ --all-namespaces > gwapi.yaml ``` The most important step is the one nobody likes: **review the output and the warnings carefully**. ingress2gateway is not a one-click migrator. It is a translator that flags what it can't translate so you can decide what to do manually. For complex setups (mTLS, custom Lua snippets, weird rewrite chains), expect to do real engineering work on top of the generated YAML. ## Picking a controller You have real choices here. The honest summary, based on the [gateway-api-bench numbers](https://github.com/howardjohn/gateway-api-bench) and the migration tooling each project ships: - **Traefik.** Lightweight, great DX, single binary, ships compatibility shims for many ingress-nginx annotations. Strong for indie teams and small clusters. Loses some throughput at very high connection counts (over 32 concurrent), but that's not where most teams live. - **Envoy Gateway.** CNCF, vendor-neutral, strict Gateway API conformance. Good for teams that want a pure standards-compliant gateway without service-mesh complexity. Built-in OIDC, rate limiting, and OAuth2. - **kgateway** (formerly Gloo Gateway). Envoy data plane, optimized control plane, ships its own fork of ingress2gateway with extra annotation coverage. AI-native routing primitives if you're building LLM apps. Best for high-churn CI/CD environments. - **HAProxy Kubernetes Ingress Controller.** Stays on the Ingress spec, not Gateway API. A reasonable choice if you want to defer the Gateway API migration and just swap the controller. - **F5 NGINX Ingress Controller.** Different project from ingress-nginx, Apache 2.0, maintained by F5. Also Ingress-spec-based. Familiar config model for teams that don't want to learn a new resource model. If your priority is the smallest possible diff to your current setup, F5 NGINX or HAProxy is the path of least resistance. If your priority is being on a maintained, modern, standards-tracking stack three years from now, Gateway API on Traefik or Envoy Gateway is the better long-term call. Don't pick based on benchmarks unless you've actually measured your own workload. ## The migration gotcha most teams hit The sneakiest bug in ingress-to-Gateway-API migrations isn't a routing rule. It's request body handling. ingress-nginx buffers request bodies by default before forwarding upstream. Most modern Gateway API controllers (Traefik, Envoy-based) stream request bodies upstream with `Transfer-Encoding: chunked`. For most apps, this is a performance win. For [WSGI apps running under uWSGI or Gunicorn](https://devoriales.com/quiz/20/gateway-api-learning-lab-from-zero-to-hero), it's a silent disaster: WSGI servers expect a `Content-Length` header and don't reliably handle chunked transfer encoding. File uploads arrive as zero-byte files. The controller logs look clean. The app logs look clean. Your users just can't upload anything. If you run any Python WSGI service, any Ruby Rack app on an older server, or any framework that predates ASGI, test file uploads on a staging cutover before you flip production traffic. The fix is usually a controller-side body buffer or a switch to ASGI/Uvicorn/Gunicorn-with-async, but you want to discover this on staging. ## A learning path If you want to go from "I read this blog" to "I can defend a Gateway API migration in a design review," there's a free 12-lesson hands-on course at [devoriales.com](https://devoriales.com/quiz/20/gateway-api-learning-lab-from-zero-to-hero) that uses Traefik and a real bookstore app on a local k3d cluster. It covers the resource model, TLS with mkcert and cert-manager, traffic splitting, ReferenceGrant, PDBs and HPAs for the gateway itself, and extending Traefik with custom Go plugins via Yaegi. About 6 to 8 hours, self-paced. The content is open; only progress tracking needs an account. It's the most practical hands-on resource we've found, and it's specifically built around the migration story instead of treating Gateway API as a greenfield exercise. ## When to stay on Ingress (for now) A few honest cases where migrating today is the wrong call: - You're running F5 NGINX Ingress Controller, HAProxy, or a cloud-managed controller (AWS ALB, GKE Ingress). Your project isn't being retired. You can wait until Gateway API gives you a feature you actually want. - You have one cluster, three services, and ingress-nginx works. Move to a maintained Ingress controller (F5 NGINX is the closest swap), table the Gateway API migration for next quarter. - You're mid-migration on something else (cluster upgrade, mesh rollout, region split). Don't stack two migrations. If you're running ingress-nginx in production with custom snippet annotations, multi-tenant clusters, or compliance requirements (SOC 2, PCI-DSS, HIPAA), the calculus is different. Unpatched CVEs in your data plane will become an audit finding fast. ## Where Ownkube fits This is exactly the kind of work a platform layer should absorb. When we provision a cluster on your AWS account, ingress is set up on Gateway API from day one, with automatic HTTPS and a ready-to-share URL for every app. No Route 53 to configure, no controller to pick, no cert-manager to wire up, no annotation soup to translate. If ingress-nginx EOL is the second migration you've had to plan this year, the answer isn't to keep getting better at migrations. It's to stop owning the parts of the stack where the answer is the same for everyone. ## Related reading - [EKS vs k3s on AWS: cost and when to choose](/blog/eks-vs-k3s-on-aws-for-startups) - [Kubernetes events disappear after an hour](/blog/kubernetes-event-monitoring-complete-guide) - [Kubernetes cost optimization for startups](/blog/kubernetes-cost-optimization-startups) If you want a Heroku-style developer experience in your own cloud account, with the ingress and edge story already solved, [deploy your first app](https://app.ownkube.io/login). BYOC Starter is free forever on one AWS instance, and Production is [$5 per vCPU plus $1 per GiB RAM per month](/pricing) when you scale. You pay AWS wholesale for the compute, we run the platform on top, and AWS Activate credits apply directly to your cloud bill. Sources: - [Ingress NGINX Retirement: What You Need to Know (Kubernetes blog)](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/) - [Ingress NGINX: Statement from the Kubernetes Steering and Security Response Committees](https://kubernetes.io/blog/2026/01/29/ingress-nginx-statement/) - [ingress2gateway 1.0 release announcement](https://kubernetes.io/blog/2026/03/20/ingress2gateway-1-0-release/) - [Gateway API Learning Lab on devoriales.com](https://devoriales.com/quiz/20/gateway-api-learning-lab-from-zero-to-hero) - [gateway-api-bench (Howard John)](https://github.com/howardjohn/gateway-api-bench) --- ## Deploy on AWS without hiring a DevOps engineer > You need to ship on AWS. You don't have a platform team. Here's the stack that actually works, and the expensive mistakes to skip on the way there. - Canonical: https://ownkube.io/blog/deploy-on-aws-without-devops-engineer - Markdown: https://ownkube.io/blog/deploy-on-aws-without-devops-engineer.md - Published: 2026-04-18 - Author: Abhishek Jaiswal - Category: How-To - Tags: aws, deployment, startups, infrastructure, k3s A senior DevOps or platform engineer in the US runs $180K to $250K all-in (base, equity, benefits, recruiting), with a 3 to 6 month lead time before they're shipping anything useful. That's roughly a year of seed-stage runway for one hire whose entire job is to keep your build pipelines green. For most early teams, it's the wrong first hire, but the work still has to happen: someone needs to deploy the app, rotate the secrets, restore the database when it goes sideways. This post is about how to do that work without making the hire. Not "how to build a platform team," and not "how to avoid Kubernetes." The opposite of both: a deliberately small AWS stack a normal application engineer can run in the background, and the four or five expensive mistakes we see teams make on the way there. It's a how-to, not a buyer-decision post. If you're still deciding whether to leave [Heroku](https://www.heroku.com) or [Render](https://render.com), start with [A Heroku alternative in your own AWS account](/blog/heroku-alternative-in-your-own-aws-account) or [Render vs your own AWS account](/blog/render-vs-aws-own-account) first, then come back here for the build-out. ## The mistakes we see every week Before the stack, the mistakes. These are the ones that eat a quarter of runway and leave you further from shipping than when you started. **Starting with [EKS](https://aws.amazon.com/eks/).** Someone reads a blog post about production Kubernetes, thinks "we should do it right," and spins up EKS with a VPC CNI, a managed node group, an ALB ingress controller, external-dns, cert-manager, and a Helm chart nobody on the team can debug. Three months later your app is still running on Heroku because nobody can get TLS working end to end. EKS is a good choice for a 30-engineer team with a platform lead. It is a terrible first step. (Full breakdown in [EKS vs k3s on AWS for startups](/blog/eks-vs-k3s-on-aws-for-startups).) **Picking [ECS](https://aws.amazon.com/ecs/) because it looks simpler than Kubernetes.** It is simpler, and that simplicity is the trap. ECS is AWS-proprietary end to end: no portable API, no Helm, no operators, no ecosystem that works anywhere else. The day you want to run your workload in another account, another cloud, or on a laptop, you rewrite every manifest. k3s gets you close to the same operational simplicity without the one-way door. **Terraforming everything from day one.** You sit down to declare your entire infrastructure in code before you've deployed anything. Two weeks in, you have a beautiful module structure and zero running services. Infrastructure as code is worth it, but not before you know what infrastructure you actually need. **Handrolling CI/CD in GitHub Actions.** A `deploy.yml` file that SSHs into an EC2 box, pulls the latest image, and runs `docker-compose up -d`. It works for three months. Then a deploy fails halfway through, leaves a container orphaned, and your API is down at 11 PM on a Friday with nobody who knows how to roll it back. **Putting production on a single EC2 instance with no plan for the day it dies.** It will die. AWS will send you a "scheduled maintenance" email, or the disk will fill up, or you'll push a bad deploy and lose the host. If you can't recreate the machine in under ten minutes, you do not have a production environment. You have a time bomb. **Treating RDS as the easy part.** RDS looks simple until you need to run a migration, restore a PITR backup to a new instance, or figure out why your connections are saturating. "Managed" does not mean "ignore it." You still need to know how to take a snapshot, how to restore one, and how to rotate credentials. ## The minimum stack Here is the smallest surface area that will actually hold up in production for a startup doing real traffic. **Compute: one or two [EC2](https://aws.amazon.com/ec2/) instances running [k3s](https://k3s.io).** k3s is a single-binary Kubernetes distribution from Rancher ([source on GitHub](https://github.com/k3s-io/k3s)). It is real Kubernetes (same API, same `kubectl`, same ecosystem) but it installs in under a minute, runs in about 500 MB of RAM, and does not require you to think about control plane sizing. For most startups, one m6i.large or m7g.large is enough to run your app, workers, and a few supporting services. Add a second node for HA when you have paying customers. **Database: Postgres, either on the same EC2 box or on [RDS](https://aws.amazon.com/rds/).** For a single-node setup with low traffic, running Postgres in-cluster on EC2 is fine. A managed Postgres operator handles backups, PITR, and failover, and you pay only for the EC2 volume. When the box starts feeling loaded or when you need multi-AZ, move to RDS with [PITR](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PIT.html) enabled, [Performance Insights](https://aws.amazon.com/rds/performance-insights/) on, and 7+ days of backups. Don't pay for multi-AZ RDS before you have revenue that justifies it. **Object storage: [S3](https://aws.amazon.com/s3/).** One bucket per environment. Enable versioning on anything you'd be upset to lose. **Secrets: [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) or [SSM Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html).** Not environment variables checked into a `.env`. Not hardcoded in your GitHub Actions secrets. Somewhere with rotation and an audit trail. **DNS, TLS, and edge protection: [Cloudflare](https://www.cloudflare.com).** Put your zone on Cloudflare's free plan and you get managed DNS, automatic TLS at the edge, DDoS protection, bot mitigation, and scrape protection, all for zero dollars. You can terminate TLS at Cloudflare and keep a simple `http://` listener inside the VPC, or terminate TLS in-cluster with [cert-manager](https://cert-manager.io) + Cloudflare DNS-01. [Route 53](https://aws.amazon.com/route53/) is a fine fallback if you're already committed to AWS-native DNS, but for most startups Cloudflare removes the DNS and WAF line items from your first-month AWS bill. **Load balancer: an [AWS Network Load Balancer](https://aws.amazon.com/elasticloadbalancing/network-load-balancer/) in front of your k3s node(s).** NLB is cheaper and simpler than ALB, and you let an ingress controller inside the cluster handle HTTP-layer routing. **Ingress: [Traefik](https://traefik.io) or [nginx-ingress](https://kubernetes.github.io/ingress-nginx/).** Either is fine. Traefik is what k3s ships with by default. **Logs: [CloudWatch Logs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html), or [Loki](https://grafana.com/oss/loki/) if you'd rather not touch CloudWatch.** Ship container stdout somewhere you can actually search it. **Observability: one [Prometheus](https://prometheus.io) + [Grafana](https://grafana.com), or a free [Grafana Cloud](https://grafana.com/products/cloud/) account.** You want to be able to answer "is the API slow right now" in 30 seconds. That's the stack. No service mesh. No GitOps tool. No multi-region. No Kubernetes operators you didn't need. When your second product engineer joins the company, they should be able to read the whole runbook in an afternoon. ## Why raw AWS gets painful You can do everything above by hand. Many startups do. The pain shows up slowly, not all at once. The first month it's fine. Then a pod gets OOMKilled, or a deploy half-succeeds, or you need to restore a database to 40 minutes ago, and nobody has the context to fix it without reading AWS docs for two hours. Then you try to spin up staging and realize your one EC2 box is a special snowflake: IAM roles added ad-hoc, security group rules nobody remembers, a DB password pasted into Slack. Reproducing it takes a week. Then a developer asks "how do I deploy my branch somewhere the designer can see it?" You don't have a good answer. Preview environments (the thing that made Heroku feel magical) require real infrastructure automation you don't have time to build. Then your first paying customer asks for an SLA and you realize you have one host, no failover, and no runbook for a bad deploy. None of these are AWS's fault. AWS gives you primitives. Turning primitives into a platform is the work, and the work is what you don't have headcount for. ## The lightweight path Here is the sequence that has worked for the startups we've watched get this right without hiring. **1. Get one EC2 box running k3s, pointed at a domain, terminating TLS.** Do this in an afternoon. Use [`k3sup`](https://github.com/alexellis/k3sup) or a 20-line cloud-init script. Put Traefik or nginx in front, point a Cloudflare DNS record at the NLB, and let Cloudflare terminate TLS at the edge. No cert-manager required for the first week. You now have a real Kubernetes cluster for about $60/month, with DDoS and bot protection already in front of it. **2. Deploy your app with a plain Deployment + Service + Ingress.** Three YAML files, maybe 80 lines total. Push a Docker image to [ECR](https://aws.amazon.com/ecr/). Apply the manifests. Your app is live on HTTPS. **3. Point RDS at it.** Create a Postgres RDS instance in the same VPC. Store the connection string in Secrets Manager. Mount it into the pod via the [Secrets Store CSI driver](https://secrets-store-csi-driver.sigs.k8s.io/), or sync it into a Kubernetes Secret with [`external-secrets`](https://external-secrets.io/). **4. Set up [GitHub Actions](https://docs.github.com/en/actions) to build, push, and `kubectl apply`.** Keep the workflow under 50 lines. Use an [OIDC role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html). Don't paste long-lived AWS keys into secrets. **5. Add a staging namespace.** Same cluster, different namespace, a `-staging` subdomain. This is the point where the cheap setup starts paying for itself. **6. Add a second node for HA.** When you have revenue. Not before. That's the lightweight path. A single engineer who is comfortable with AWS and Kubernetes basics can build it in two weeks. It will hold you to about 50 engineers and tens of thousands of requests per minute. Past that you need real investment, but by then you can afford it. ## What to automate first You cannot automate everything. Here's the priority order when you have limited engineering time. **Deploys.** If a deploy takes more than five minutes or requires a remembered command sequence, engineers start batching changes to avoid it, and ship buggier code. Automate `git push` to live in under five minutes. Nothing else matters more. **TLS.** Certificates that expire on a Sunday morning are a self-inflicted outage. cert-manager with Let's Encrypt, automatic renewal, and alerts on renewal failure. Set it up once, never touch it. **Logs.** If you cannot search yesterday's logs from one place in under 30 seconds, you are flying blind during incidents. Ship everything to CloudWatch Logs or Loki on day one. It is much harder to retrofit later. **Rollback.** Every deploy needs a one-command rollback. `kubectl rollout undo deployment/api` is fine. What is not fine is "rebuild the previous image, repush, redeploy, hope." Rehearse the rollback before you need it. **Backups.** RDS automated backups are on by default, but useless if you've never tested a restore. Once a quarter, restore the latest snapshot to a new instance and run your app against it. The first time, you'll find something broken. Better now than during an incident. Logs, alerts, dashboards, IaC, preview environments, cost monitoring. All of these matter. But the five above are the ones that keep you alive. Do them first, in order. ## Where this breaks down This path works until one of four things is true: 1. You have more than about 50 engineers and the cognitive overhead of "everyone knows the stack" stops scaling. 2. You have compliance requirements (HIPAA, FedRAMP) that demand a level of audit logging and network isolation a single k3s node can't easily provide. 3. You have traffic that genuinely needs multi-region. 4. You are running stateful workloads (databases, queues) inside Kubernetes, in which case you need someone whose full-time job is to care about them. Until then, a single k3s cluster on EC2 with RDS for data, S3 for blobs, and Secrets Manager for credentials is enough. Everyone who tells you otherwise is either selling you something or works somewhere big enough that their advice doesn't apply to you. ## The shortest path The setup above is what we install for you. Ownkube runs inside your own AWS account, wires in TLS, logs, backups, deploy automation, and a ready-to-share URL, and then gets out of the way. Your AWS credits still apply. The infrastructure is vanilla enough that if you ever want to fire us, everything keeps running. If you'd rather skip the AWS account for now, Ownkube Compute runs the same workflow on hosted infrastructure with no cloud account required. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. If you are staring at a migration from Heroku and the idea of hiring a platform team is not realistic, [deploy your first app](https://app.ownkube.io/login) and we'll have you live on your own AWS account this week. No DevOps hire required. --- ## Render vs your own AWS account: when to switch > Render is great early on. For teams needing infrastructure control, AWS ownership, and predictable costs, your own account wins. How to decide. - Canonical: https://ownkube.io/blog/render-vs-aws-own-account - Markdown: https://ownkube.io/blog/render-vs-aws-own-account.md - Published: 2026-04-11 - Author: Abhishek Jaiswal - Category: Engineering - Tags: render-alternative, aws, paas, platform-engineering, cost [Render](https://render.com) is a good platform. A lot of teams ship their first production app on it and never have a complaint. But if you're reading a post called "Render vs AWS," you're probably not one of those teams anymore. You've hit something: a bill that stopped making sense, a security review that asked where the data lives, a networking requirement Render doesn't cover. Now you're trying to figure out whether to stay or move. This post is the buyer-decision angle: a side-by-side comparison, the team sizes where each side wins, and the honest cost of owning AWS yourself. If you're already past the comparison and just want the architecture for a Heroku-style platform on AWS, see [A Heroku alternative that runs in your own AWS account](/blog/heroku-alternative-in-your-own-aws-account). If your real question is "how do I do AWS without hiring a DevOps engineer," that's a [different post](/blog/deploy-on-aws-without-devops-engineer). Short answer: if you're pre-PMF or under five engineers, stay on Render. If you're a B2B SaaS team with growing AWS credits, a real compliance surface, or workloads that keep bumping into platform limits, running in your own AWS account is almost certainly the better long-term answer. The middle ground is a Render-like workflow on AWS infrastructure you own. The rest of this post walks through how to know which side of the line you're on. ## Render vs your own AWS account at a glance | Dimension | Render | Your own AWS account | |---|---|---| | Setup speed | Minutes. Connect a repo, done. | Longer out of the box. Minutes with a platform layer on top. | | Infrastructure control | Limited to what Render exposes. | Full access. Any AWS service, any region, any instance type. | | Networking flexibility | Basic private networking, managed. | VPC peering, Transit Gateway, PrivateLink, Direct Connect, all available. | | Security boundary ownership | Render's shared infrastructure. | Your VPC, your KMS keys, your IAM, your audit logs. | | Pricing predictability | Per-service pricing, markup over raw compute. | Wholesale AWS pricing. Credits apply. Savings Plans apply. | | Operational burden | Render handles most of it. | You own it, unless you put a platform layer on top. | | Migration difficulty | Low from zero. Higher later, because you've built to their primitives. | Higher upfront. Lower in perpetuity because the infra is yours. | | Compliance story | "Our PaaS provider is SOC 2." | "Our data runs in our VPC under our controls." | The table is honest. There are real reasons to stay on Render and real reasons to leave. The question is which set applies to you right now. ## Why do teams choose Render in the first place? It's worth stating clearly, because Render does something well that AWS on its own doesn't. **It's fast.** You connect a GitHub repo, pick a service type, and get a URL. No IAM roles, no VPCs, no load balancer to configure. For the first ninety days of a product, nothing AWS-native comes close to that velocity. **The defaults are good.** TLS certs, zero-downtime deploys, health checks, autoscaling, a managed Postgres that actually works, all wired up without reading a single piece of documentation. **No infra team required.** Two backend engineers can run a production business on Render without ever touching a cloud console. For seed-stage teams, that's not a nice-to-have. It's the difference between shipping and not shipping. **The developer experience is coherent.** One dashboard, one mental model, one place logs and metrics show up. Engineers onboard in an afternoon. If the above is everything you need, Render is a perfectly respectable home. Don't let anyone talk you into a migration for the sake of sophistication. ## When does Render stop fitting? Nobody leaves Render because they stopped liking it. They leave because the shape of the business changed. **The bill stops being proportional.** Render's pricing is fine at small scale. But once you're running a dozen services, a couple of background workers, and a Postgres with non-trivial storage, the per-service billing adds up faster than the equivalent EC2 + RDS would. On a growing app the delta can cover a senior hire. **Procurement asks harder questions.** When an enterprise customer's security team asks for a SOC 2 report, that's easy. When they ask where customer data is processed, whether you control the encryption keys, and whether you can support a customer-specific region, "we use Render" starts generating follow-up emails. **You need networking Render doesn't expose.** VPC peering to a partner's account. A PrivateLink endpoint to a vendor's API. A customer-dedicated VPC. A specific region for data residency. These are table stakes on AWS and either unavailable or awkward on shared PaaS. **Your AWS credits are stranded.** Most funded startups have six figures of [AWS Activate](https://aws.amazon.com/activate/) credits. If the platform bill goes to Render, those credits are just sitting there, depreciating with the runway. **The lock-in gets heavier over time.** Render services, Render disks, Render's deploy model. The longer you build on them, the more expensive leaving becomes. Teams that start thinking about portability at month six are in a much better position than teams that start thinking about it at month thirty-six. **The workload outgrows the platform.** GPUs, spot fleets, long-running batch jobs, specialized instance types, large private datasets. At some point you need a primitive Render doesn't have, and the workaround is worse than just running it on AWS. None of these are Render's fault. Shared PaaS always makes the same tradeoff: simplicity now for flexibility later. You're asking the question because the tradeoff stopped working. ## What the full stack actually costs The bill argument is easy to wave away until you price a whole app instead of a web service. A real app on Render is a web service plus a managed Postgres plus a cache, and Render bills each as its own line with a markup over raw compute. Take one small always-on stack, a 1 vCPU / 1 GiB web app, a 1 GiB / 10 GB Postgres, and a small cache, and compare three ways to run it: | Monthly cost, small full stack | Render | Ownkube Compute | Your own AWS (k3s on EC2) | |---|---|---|---| | Web app (1 vCPU / 1 GiB, always on) | ~$25 | ~$12 | included in the instance | | Managed Postgres (1 GiB / 10 GB) | ~$22 | ~$11 | included in the instance | | Cache (small Redis or Valkey) | ~$10 | ~$5 | included in the instance | | Underlying AWS instance | n/a | n/a | ~$30 (one t3.medium) | | **Total** | **~$57** | **~$27** | **~$30 of AWS + a platform layer** | Read the numbers as illustrative, mapped to each provider's nearest real tier as of October 2026. Two things stand out. Ownkube Compute runs the same stack for well under half the Render bill, because it meters the app per minute and runs Postgres and the cache on near-cost boxes (a database box from $4 a month plus storage, a Valkey cache box from $2) rather than marked-up add-ons. And once you own the AWS instance, the database and cache ride along on hardware you already pay for, so the marginal cost of adding them is close to zero. The crossover is real: a small managed stack is cheapest on Ownkube Compute, while a growing fleet of services and a large database eventually favors the instance you own outright. That is the whole arc of this post, and it is why Ownkube runs the same app on managed compute or in your own AWS account with no rewrite between them. ## When running in your own AWS account makes sense Be specific here, because "move to AWS" is advice that costs teams real money when it's wrong. The honest answer depends heavily on team size, so we'll break it down. **If you're an indie builder or a 1 to 2 person team just trying things out**, don't migrate. Stay on Render, or if you really want AWS ownership, run k3s on a single EC2 box and call it done. You don't have the headcount to absorb the operational surface of a full AWS setup, and Render's simplicity is worth more to you than control right now. **If you're a small team (up to 20 engineers) without a dedicated platform owner**, running in your own AWS account is the right call if most of these are true: - You're selling B2B, and security questionnaires already mention AWS. - You have AWS credits you actually want to use. - You want the option to walk away from any vendor without rewriting deployments. - The Render bill is approaching the cost of a senior engineer. At this size, pick k3s on EC2 inside your own AWS account with a platform layer on top (on Ownkube, this is the BYOC Starter tier, free forever on one instance). You get the ownership story without the EKS complexity, and you can move to EKS later without replatforming your apps. **If you're 20+ engineers with production traffic or real compliance pressure** (SOC 2 Type II, HIPAA, ISO 27001, FedRAMP-adjacent work), pick a production-grade setup on AWS. Not as a hedge, as a commitment. At that size the managed control plane, IRSA, multi-AZ autoscaling, and full AWS observability pay for themselves. EKS becomes the default, not an upgrade (Ownkube's BYOC Production tier, metered at $5 per vCPU plus $1 per GiB RAM per month). If three or four of the bullets above apply to you, the sooner you start the migration, the cheaper it is. ## The hidden tradeoff "Just move to AWS" is not free advice. Raw AWS gives you everything and organizes none of it. You have to decide on a cluster or no cluster, a CI/CD approach, a secrets strategy, a logging pipeline, a cost monitoring setup, and a dozen other choices before the first deploy. Teams that go straight from Render to hand-rolled AWS often spend a quarter not shipping product while they wire it up. This is where a lightweight platform layer on top of AWS earns its keep. The goal is to keep the Render-shaped developer experience (git push, managed database, a ready-to-share domain, observability baked in) while the infrastructure itself lives in your account. Concretely, that usually means: - A small Kubernetes distribution like **[k3s](https://k3s.io)** on EC2 for stateless workloads. Boots in seconds, runs on small instances, much smaller operational surface than EKS. - **State handled sensibly.** For indie builders and small teams, a managed Postgres running in-cluster on your own EC2 (operated by an operator, not DIY) is a perfectly good default. As you grow into production scale, move to **managed AWS services** for stateful workloads: Multi-AZ managed Postgres, a Valkey cache, S3 for artifacts. The point is to not self-host what someone else already operates well. - **A deployment control plane** that turns `git push` into a running service without anyone writing a manifest. - **A clear upgrade path.** When the team grows and you do want EKS or a full platform team, the workloads don't move. Only the control plane underneath them. This is deliberately boring. The point is not to showcase infrastructure. The point is to keep the workflow your engineers already like, running on infrastructure you control. ## When should you stay on Render? To be fair (and because the wrong migration is worse than no migration) here's when Render is still the right answer: - **You're pre-PMF.** Every hour spent on infrastructure is an hour not spent on the thing that might actually work. - **Your team is three or fewer.** You don't have the headcount to absorb even a well-abstracted AWS setup. - **Your workload is web-shaped and small.** A couple of services, one database, no meaningful compliance surface. - **Your horizon is short.** If the next six months are about customer discovery, not scaling, stay where you are. - **You don't have AWS credits or a strong AWS preference.** Going to AWS just to go to AWS is not a reason. "Stay on Render" is a completely respectable answer. We tell indie devs and 1 to 2 person teams this all the time. ## A decision checklist for switching If you're still on the fence, count how many of these apply to you: - [ ] Our Render bill is larger than one senior engineer's monthly comp. - [ ] We have unused AWS credits we could apply. - [ ] Customers are asking where their data runs or demanding their own region. - [ ] We're working on SOC 2, HIPAA, or a similar compliance framework. - [ ] We've needed networking features Render doesn't support (VPC peering, private links, specific regions). - [ ] We expect to be on AWS in two years regardless, and would rather start now than migrate later. - [ ] We want a real exit plan from our current platform vendor. Three or more checked boxes means it's probably time to start planning. Five or more means you're already past the point where the move is cheap. **Reference:** For the step-by-step version, see [Migrate from Render](/docs/migrate/from-render) in the docs. ## Conclusion Render is good at what it does. For early teams, it's one of the fastest ways to get something real into production. But the same simplicity that makes it great at the start becomes a ceiling later: on cost, on control, on compliance, on what the architecture is allowed to look like. For teams that hit that ceiling, running in their own AWS account is usually the right long-term answer. The honest catch is that raw AWS introduces its own overhead, and not every team has the time or headcount to absorb it. The middle ground is a managed, opinionated platform layer on top of AWS you own. That's what most of these migrations actually look like. We walk through that architecture in detail in [A Heroku alternative that runs in your own AWS account](/blog/heroku-alternative-in-your-own-aws-account), and the EKS-vs-k3s call inside it in [EKS vs k3s on AWS for startups](/blog/eks-vs-k3s-on-aws-for-startups). If you want Heroku/Render-like deploys in your own AWS account, [Ownkube](https://app.ownkube.io/login) is built for that. Connect your AWS account, get a git-push workflow, managed Postgres, a ready-to-share URL, and automatic TLS and rollbacks, with the infrastructure, the data, and the exit in your name. Free forever on BYOC Starter, [$5 per vCPU plus $1 per GiB RAM per month](/pricing) on BYOC Production when you scale. Fifteen minutes to your first deploy. Happy to [talk through your situation](https://app.ownkube.io/login) if you're not sure which side of the line you're on. --- ## EKS vs k3s on AWS: cost and when to choose > A direct comparison of EKS and k3s on AWS for small teams who have to ship. Real bills, real failure modes, and the line where k3s stops being enough. - Canonical: https://ownkube.io/blog/eks-vs-k3s-on-aws-for-startups - Markdown: https://ownkube.io/blog/eks-vs-k3s-on-aws-for-startups.md - Published: 2026-04-04 - Author: Abhishek Jaiswal - Category: Engineering - Tags: kubernetes, eks, k3s, aws, startups, cost You have an app to ship. Maybe a few. You're on AWS because that's where the credits are and where the auditors want your data. Somebody on the team said "Kubernetes" out loud and now you're trying to decide between EKS and k3s before the week is out. We'll save you the essay. Here's our take: - **If you're an indie builder or a 1 to 2 person team just trying the product out**, run k3s on a single EC2 box. Don't even open the EKS console. The $73/month control plane fee alone is larger than your entire compute bill, and none of the things EKS is good at are things you need yet. - **If you're a small team (up to 20 engineers) without a dedicated platform owner**, start on k3s. Ship product. You'll know when to graduate. - **If you're 20+ engineers with production traffic, multi-AZ requirements, or real compliance pressure**, pick EKS. Not as a hedge, as a commitment. At that size the managed control plane, IRSA, and AWS-native autoscaling pay for themselves. EKS is usually the right destination, not the right starting point, and the reason has very little to do with Kubernetes itself. Here's what actually matters when you make this call. ## The thing people get wrong Both are Kubernetes. Same API, same manifests, same `kubectl apply`. If someone hands you a chart that works on EKS, it will almost certainly work on k3s without modification, and vice versa. This isn't a technology choice. It's an operations choice. What you're really picking is **who owns the control plane and how much of AWS you need to glue in**. - **[EKS](https://aws.amazon.com/eks/)**: AWS runs the control plane. You pay $73/month per cluster for it, and you glue in [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), [VPC CNI](https://github.com/aws/amazon-vpc-cni-k8s), the [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/), [EBS CSI](https://github.com/kubernetes-sigs/aws-ebs-csi-driver), and whichever flavor of autoscaler you prefer. Upgrades happen on AWS's calendar. - **[k3s](https://k3s.io)**: You run the control plane on an EC2 instance. It starts in under a minute, ships with [Traefik](https://traefik.io) and a working storage class, and upgrades when you decide. Nothing AWS-specific unless you want it. The rest of this post is the operational fallout of that choice. ## Side-by-side, the useful version | | EKS | k3s on EC2 | |---|---|---| | Control plane cost | $73/month/cluster | $0 | | Minimum viable footprint | 1 control plane + 2 nodes in 2 AZs | 1 EC2 instance | | Time to a working cluster | 15-25 min with `eksctl`, longer first time | Under 60 seconds | | Networking | VPC CNI (real VPC IPs, counts against subnet) | Flannel VXLAN (overlay, doesn't touch VPC IPs) | | Ingress | ALB Controller + one ALB per group | Traefik built in | | Storage | EBS / EFS / FSx CSI, IAM required | `local-path` out of the box; add EBS CSI if you want | | Pod IAM | IRSA (clean, audited) | Instance profile, or you bring a solution | | Upgrades | AWS-driven, rolling managed node groups | You pick the hour; risk is yours | | Biggest sharp edge | VPC CNI IP exhaustion, IRSA permission dance | Embedded etcd quorum loss, single-node backups | | Honest team size | 15+ engineers, platform owner forming | 2-20 engineers, nobody owns infra full-time | Anything not on this table is a rounding error for a startup. ### A note on [ECS](https://aws.amazon.com/ecs/) ECS often shows up as "Kubernetes but simpler." The simplicity is real; the tradeoff is lock-in. Task definitions, services, and the deployment model are AWS-proprietary: no portable API, no Helm, no ecosystem that transfers. The day you want to run the same workloads elsewhere, you rewrite every manifest. Both EKS and k3s give you the same portable Kubernetes API; ECS gives you a one-way door. ## What the bill really looks like Take a workload we see often: one web service, two workers, Postgres on RDS, Redis on ElastiCache, a staging environment, and room for a few PR preview environments. Call it ~8 vCPU / 16GB of production pods plus ~4 vCPU / 8GB for staging and previews. All prices below are approximate on-demand rates in `us-east-1` as of April 2026. Your numbers will vary with region, reserved capacity, and traffic profile. ### k3s on EC2 - 3 × `t3.xlarge` on-demand (4 vCPU / 16GB each): **$299** - 300GB EBS gp3: **$24** - 1 NAT gateway: **$32 + traffic** - 1 ALB in front of Traefik: **$22** - Data transfer (moderate): **$20** **~$400/month** for plenty of headroom. You can fit 5-10 services, full staging, and rotating previews on that without thinking about capacity. ### EKS - Control plane: **$73** - 2 × `t3.large` prod nodes (for the 2-AZ story): **$121** - 1 × `t3.medium` staging node: **$30** - 1 × `t3.large` for previews: **$60** - 300GB EBS gp3: **$24** - 1 NAT gateway: **$32 + traffic** - 2-3 ALBs (ALB controller per IngressGroup, typical): **$44-$66** - Data transfer (higher, because VPC CNI loves inter-AZ chatter): **$25** **~$410-$440/month.** Pricing looks close until you add the labor, the part nobody puts on the slide: - First EKS setup: 1 to 3 engineer-days. - Every EKS upgrade: a half-day of drain and verify, quarterly. - First VPC CNI IP exhaustion: half a day figuring out `t3.large` nodes only get 35 IPs. - ALB controller version pinning across upgrades: a ticket, every time. That's real salary spent on AWS glue rather than product. On k3s, the comparable surface area is "keep an AMI up to date" and "snapshot etcd nightly." ## Getting from zero to shipping **EKS** (plan on a week if nobody's done this before): 1. VPC with public/private subnets across 2 to 3 AZs, plus NAT and route tables. 2. IAM roles for the cluster, node group, and IRSA. 3. [`eksctl create cluster`](https://eksctl.io), then wait 15 to 25 minutes. 4. Install the AWS Load Balancer Controller, EBS CSI driver, and [Karpenter](https://karpenter.sh) (each its own IAM dance). 5. Tune CoreDNS, metrics-server, and VPC CNI (prefix delegation, warm pool settings). 6. Wire up Ingress with [ACM](https://aws.amazon.com/certificate-manager/) and [Route53](https://aws.amazon.com/route53/). The piece that bites first-timers is always IRSA and the OIDC provider. One typo in a trust policy and pods silently fail to assume roles. **k3s** (under a day): 1. Launch an EC2 instance. 2. `curl -sfL https://get.k3s.io | sh -`. 3. Point DNS at it. 4. Deploy. Not a marketing simplification. Traefik is running, there's a default StorageClass, kubeconfig is at `/etc/rancher/k3s/k3s.yaml`. You can be serving traffic in a lunch break. For production, add an ASG of three k3s servers with embedded etcd for HA, an NLB out front, and a nightly etcd snapshot. ## The failures you'll actually hit Forget feature checklists. These are the incidents that will eat your weekend. **On EKS, the usual suspects:** - **Pods stuck Pending with `no IP addresses available`.** VPC CNI assigns real VPC IPs to every pod. On a `t3.large` that's 35 IPs max. You hit it during an autoscaling event, not during testing. Fix is prefix delegation, which requires a node recycle. - **IRSA silently not working.** Pod annotation, service account annotation, trust policy, OIDC provider, role policy. Five things have to line up. One off-by-one and you get `AccessDenied` with no obvious source. - **ALB controller version skew after an EKS upgrade.** The ALB controller has its own compatibility matrix. Forget to bump it and ingress just stops reconciling. - **Node group upgrade drains in the wrong order.** PDBs not set, pods evicted faster than they start elsewhere. 30-second outage during a "safe" upgrade. **On k3s, the usual suspects:** - **Embedded etcd quorum loss.** You were running HA on three `t3.medium` servers. Two got replaced by ASG inside five minutes. Cluster is read-only. Recovery is `k3s server --cluster-reset` from a known-good snapshot. You want that snapshot script working before you need it. - **Local-path PVs disappearing with the node.** The default StorageClass is per-node local disk. Great for caches, terrible for your single-replica Postgres. Switch stateful workloads to RDS or add EBS CSI. - **k3s version upgrade breaking Traefik.** k3s bundles Traefik, and major k3s upgrades can bump Traefik's CRDs. Pin the Traefik Helm values or disable the bundled version and run your own. - **Single-node cluster dies with the instance.** If you started all-in-one to move fast and forgot to migrate to HA, a spot interruption or AZ blip is a full outage. Migrate before you're depending on it in production. Both sets are learnable. The EKS failures are more about fighting AWS primitives. The k3s failures are more about owning the operational basics yourself. ## When k3s is enough Start here if most of these are true: - Under 20 engineers and nobody's job title is "platform." - Stateless web services and workers, with state in a managed Postgres (either in-cluster on EC2 with an operator, or RDS if you prefer AWS-native), plus ElastiCache and S3 as needed. - Single-region is fine for now. - Compliance doesn't demand AWS-managed control plane components. - You'd rather spend the next two sprints on product than on Kubernetes. k3s is not a toy. It's CNCF-certified Kubernetes, it powers Rancher's own product, and there are companies running it on bare metal fleets larger than most SaaS startups will ever see. Using it isn't a compromise; it's picking the distribution that doesn't punish small teams. ## When EKS earns its keep Move to EKS (or start there, if you're already past the line) when any of these are real: - **Audit pressure.** If SOC 2 or HIPAA readiness hinges on "AWS patches the control plane," use EKS. - **Fine-grained pod IAM.** Per-pod credentials for S3, SQS, Bedrock are much cleaner with IRSA than instance profiles or sidecars. - **~50+ services or ~300+ pods.** k3s handles it, but upgrades and capacity get real. Karpenter on EKS is genuinely better at that scale. - **Multi-AZ or multi-region HA as a hard requirement.** k3s HA is possible; EKS HA is the default. - **A dedicated platform hire or team.** Once someone owns infra full-time, they'll want managed node groups and IRSA. - **GPU pools, Graviton spot fleets, Bottlerocket, Windows nodes.** EKS wires these in natively. If none of these describe your next 12 months, you're paying EKS tax for a future you might not have. ## The migration nobody sells you Here's what actually happens when you outgrow k3s and move to EKS. Your Deployments, Services, ConfigMaps, Secrets, Jobs, CronJobs: unchanged. Your Helm charts: unchanged. The diff is at the edges: - Ingress: Traefik annotations become ALB controller annotations (10-30 lines of YAML per service). - Storage: `local-path` PVCs move to `gp3`; stateful workloads you already had on RDS need no change. - IAM: instance-profile or sidecar-based access becomes IRSA. A real piece of work, but mechanical. - Autoscaling: single-ASG k3s becomes Cluster Autoscaler or Karpenter. Plan a week or two of cleanup, not a rewrite. And critically, k3s-to-EKS is a much shorter migration than "no Kubernetes to EKS" would have been if you'd held off for 18 months. For a deeper walkthrough of the k3s-on-AWS setup most startups land on, see [How to deploy on AWS without hiring a DevOps engineer](/blog/deploy-on-aws-without-devops-engineer). ## The real recommendation If you're choosing today on AWS with under 20 engineers, start on k3s. Ship product. Re-evaluate when a specific thing on the EKS list above becomes true, not sooner. You'll save money and keep your team's attention on the business, and you won't paint yourself into a corner because the migration path is honest and well-worn. [Ownkube](https://ownkube.io) runs on top of either cluster and handles the part neither EKS nor k3s gives you out of the box: the Heroku-style developer flow. Git push to deploy, a ready-to-share URL with automatic HTTPS, one-click Postgres and a Valkey cache in your VPC, and logs, metrics, and rollbacks built into the dashboard. Start on BYOC Starter in your own AWS account, scale to Production when your business asks for it, keep the same workflow the whole way through. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. If you'd rather skip the setup, [deploy your first app](https://app.ownkube.io/login) and we'll have you live in the time it took to read this. --- ## A Heroku alternative in your own AWS account > Keep the Heroku workflow (git push, managed Postgres, a ready-to-share URL) in an AWS account you own. How teams do it and what changes. - Canonical: https://ownkube.io/blog/heroku-alternative-in-your-own-aws-account - Markdown: https://ownkube.io/blog/heroku-alternative-in-your-own-aws-account.md - Published: 2026-03-21 - Author: Abhishek Jaiswal - Category: Engineering - Tags: heroku-alternative, aws, paas, platform-engineering Somewhere around your third production incident on [Heroku](https://www.heroku.com), whether it's the 2 AM dyno that OOM-killed with no explanation or the compliance officer who asked where customer data actually lives, you start pricing alternatives. For most teams we talk to, the honest answer isn't "move to [Render](https://render.com)" or "move to [Railway](https://railway.com)." Those are lateral moves. Same shared infrastructure, same markup, same awkward conversation with your auditor next quarter. The real answer is: keep the workflow you loved (git push, addons, review apps, one mental model), but run it inside an AWS account you own. That's what this post is about. Specifically: how teams replace each piece of the Heroku experience with something that lives in their own VPC, what they keep, what they trade away, and the two reference architectures that actually work. If you're trying to compare PaaS-to-PaaS instead, we have a separate post on [Render vs your own AWS account](/blog/render-vs-aws-own-account); if you're trying to ship on AWS without hiring a platform engineer, [start here](/blog/deploy-on-aws-without-devops-engineer). ## What people loved about Heroku in the first place It's worth stating plainly, because a lot of the industry has forgotten. Heroku was good. For a long time, it was the best developer experience in the business. You pushed git, you got a URL, you could add a Postgres, and you could go back to writing the product. Specifically, what made it special: - **Git push as a deploy primitive.** No build server to babysit, no container registry to configure. The VCS was the API. - **Opinionated defaults.** You didn't pick a base image or a log driver. Someone else had already argued about those so you didn't have to. - **Managed datastores without a ticket.** `heroku addons:create heroku-postgresql` and you had a production DB with backups in 30 seconds. - **Review apps.** Every PR got a URL. Designers reviewed real builds. Sales demoed features that hadn't merged yet. - **A single mental model.** Dynos, add-ons, releases. A new engineer was productive in an afternoon. That package (velocity without decision fatigue) is what teams actually miss when they leave. They don't miss Heroku. They miss that shape of platform. ## Why teams leave shared PaaS anyway Nobody leaves Heroku because they stopped liking it. They leave because it stops fitting. **The bill stops making sense.** A 2x Performance-L dyno runs around $500/month on Heroku's published rates (April 2026). The equivalent on bare EC2 is closer to $150. On a small app the delta is a rounding error; on a growing app it's a full engineering hire. **Compliance conversations get harder.** When your customer's security review asks where their data is processed, "Salesforce-owned shared infrastructure" is a slower answer than "our VPC in us-east-1." SOC 2, HIPAA, and most enterprise procurement forms want the second one. **Your AWS credits are stranded.** Funded startups often carry six figures of [AWS Activate](https://aws.amazon.com/activate/) credits sitting unused because their platform bills go somewhere else. That's runway you're setting on fire. **You hit architectural ceilings.** Private networking to a data lake, GPU workers, [VPC peering](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html), long-running batch jobs, [spot instances](https://aws.amazon.com/ec2/spot/) for background work. These are either impossible or painful on shared PaaS. (If you're weighing Render specifically, we wrote up the tradeoffs in [Render vs your own AWS account](/blog/render-vs-aws-own-account).) **The escape plan is the real problem.** Even if none of the above mattered today, the day you outgrow the platform you're stuck rewriting how every service deploys, how every secret is injected, how every cron runs. There's no graceful exit. So teams start sketching an alternative. And the sketch is almost always the same: _give me Heroku, but running in an AWS account I control._ ## What actually changes when the account is yours This is the part that gets glossed over in most comparison posts. Owning the AWS account is not a cosmetic change. It changes five concrete things. **1. The data never leaves your perimeter.** Your Postgres, your object storage, your Redis, all provisioned in your VPC, in your region, under your KMS keys. Auditors stop asking follow-up questions. DPAs get shorter. **2. You pay wholesale.** There's no per-dyno markup between you and EC2. A c7g.large is a c7g.large. Savings Plans apply. Spot applies. Credits apply. The unit economics look like an AWS bill because they are one. **3. You inherit AWS's surface area.** Need a private link to an RDS instance a customer gave you access to? Fine. Need a specific instance family for a vendor's licensing? Fine. You're no longer constrained to the intersection of features your PaaS happened to expose. **4. The exit is free.** If you decide to replace the platform layer, the workloads don't move. The databases don't move. The DNS doesn't move. You're disconnecting a control plane, not migrating. **5. You become the operator.** This is the honest downside. Someone has to patch nodes, rotate certs, upgrade the orchestrator, watch for CVEs. The whole point of a Heroku-style platform in your own AWS account is to make that someone a piece of software, not a team of three. ## Two on-ramps, and a reference architecture for the one that runs in your AWS account Ownkube gives you two co-equal ways in. Ownkube Compute is hosted: push a repo or a container and get a live URL in seconds, no AWS account required. Bring your own cloud (BYOC) runs the same workflow inside an AWS account you control, starting free on a single instance and scaling into a multi-node setup as you grow. The rest of this section is about BYOC, since that's the AWS-account architecture this post is walking through. Both on-ramps are deliberately boring. We're not reinventing AWS, we're just making it feel like Heroku. ### BYOC Starter: the default for indie and small teams ![Ownkube BYOC Starter reference architecture on AWS: HTTPS traffic on a ready-to-share address routed to a single k3s cluster on EC2 with managed Postgres on the same EC2 node, S3 for artifacts, Secrets Manager and CloudWatch, driven by the Ownkube SaaS control plane outside the customer account.](/diagrams/heroku-alternative-reference.png) This is the shape most teams land on first. Single k3s cluster, a handful of EC2 nodes in an autoscaling group, Postgres running on the same EC2 managed by Ownkube, S3 for artifacts. That's it. **Why [k3s](https://k3s.io) here, not [EKS](https://aws.amazon.com/eks/).** EKS has a $73/month control-plane bill before you've launched a pod. k3s is a single binary, boots in seconds, has a smaller CVE footprint, and runs happily on t4g/c7g with 1 to 2 GB of RAM overhead per node. For indie developers, side projects, and small-team dev environments, k3s removes the AWS tax without removing real Kubernetes underneath. (Full technical comparison: [EKS vs k3s on AWS for startups](/blog/eks-vs-k3s-on-aws-for-startups).) **Why Managed Postgres on your EC2, not [RDS](https://aws.amazon.com/rds/).** For low-traffic apps, RDS is a tax. A db.t4g.micro RDS instance starts at about $12/month; the equivalent Postgres colocated on an EC2 node you're already paying for is free on top of the compute. Ownkube operates it like a managed service (backups, PITR, failover) but the bytes sit on your EBS volumes. When the workload grows past what one box can carry, you flip to a Multi-AZ managed Postgres with one click. No migration tooling required. **Why not [ECS](https://aws.amazon.com/ecs/).** ECS is tempting because it's "simpler than Kubernetes," but the simplicity is bought with lock-in. Task definitions, services, and the deploy model are AWS-proprietary: no portable API, no `kubectl` anywhere else, and none of the Kubernetes ecosystem (Helm, operators, ingress controllers) transfers. The day you want to move a workload to another cloud or another account, you rewrite every manifest. That's the exact opposite of the portability we're optimizing for here. k3s gives you most of ECS's operational simplicity without the one-way door. **Why an autoscaling group.** Stateless web workloads and background jobs run across a small autoscaling group that right-sizes instance count to traffic, with dedicated on-demand capacity reserved for the Postgres node. You're not paying for idle capacity around the clock. ### BYOC Production: the scale tier Once you're past a single-node workload (multi-AZ uptime requirements, multiple teams deploying in parallel, heavy observability needs, or enterprise procurement asking about managed control planes), Ownkube flips the same application into BYOC Production, backed by EKS. Same git push, same ready-to-share address. Different substrate. ![Ownkube BYOC Production reference architecture on AWS: HTTPS routed through an ALB into a multi-AZ EKS cluster with managed control plane and autoscaled node groups, backed by Multi-AZ managed Postgres with point-in-time recovery, a Valkey cache, S3, Secrets Manager (IRSA), and CloudWatch/OTel, all driven by the Ownkube SaaS control plane outside the customer account.](/diagrams/heroku-alternative-eks-scale.png) The upgrade is additive, not a rewrite: EKS replaces k3s, Multi-AZ managed Postgres and a Valkey cache replace the in-cluster Postgres, an ALB takes the incoming traffic, and CloudWatch + OTel get wired in end-to-end. Secrets Manager uses [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) for workload identity. You keep your application manifests. **Why the control plane is split.** On both tiers, the Ownkube control plane (the thing that shows you deploys and handles git pushes) lives in our SaaS. It holds no application data, no database credentials, no secrets. Everything that actually matters runs in your account. If our control plane disappeared tomorrow, your apps would keep serving traffic. ## You don't set up DNS A detail worth calling out, because it's one of the reasons the developer experience feels closer to Heroku than to raw AWS: every app gets an address like `your-app.ownkube.app` with automatic HTTPS. You can send that URL to customers, teammates, or stakeholders the same afternoon you connect your cloud. No domain purchase, no nameserver hand-off, no "waiting for DNS to propagate," and no certificates to rotate. On a Starter cluster you can attach your own domain whenever you're ready. The workflow on top of this is the part that should feel familiar. Connect a repo. A push to `main` triggers a build, and the resulting image rolls out behind a health check with an automatic rollback if it fails. Logs, metrics, and errors flow into one place. Secrets are injected at runtime from Secrets Manager. Nobody on your team writes a deployment manifest. ## Who this setup is actually for Be honest about this, because the wrong fit is expensive for everyone. **This is a strong fit if:** - You have 10 to 100 engineers and a growing AWS bill. - You've outgrown (or are about to outgrow) Heroku, Render, or Railway on cost, compliance, or capability. - You have AWS credits you want to use. - You don't have a dedicated platform team and don't plan to hire one for another 12 to 24 months. - You care about portability. You want the option to walk away from any vendor, including us, without rewriting your deployment story. **This is probably not a fit if:** - You're a solo developer or pre-PMF team shipping a weekend project. Heroku or Railway will get you there faster. - You already have a mature internal platform team with strong opinions. You don't need us; you need [Crossplane](https://www.crossplane.io/) and time. - You require on-prem or sovereign cloud deployments today. We're AWS-first, with GCP and Azure coming. - Your workload isn't web-shaped (heavy HPC, low-latency trading, specialized hardware). Those deserve a purpose-built stack. If you're in the sweet spot, the calculus is usually straightforward: the platform costs less than one platform engineer, gets you back to a Heroku-shaped workflow, and leaves the infrastructure in your name. **Reference:** For the step-by-step version, see [Migrate from Heroku](/docs/migrate/from-heroku) in the docs. ## Where Ownkube fits You can build the architecture above yourself. People do. It takes six to twelve months, one to three senior engineers, and ongoing maintenance that never really ends. That's a reasonable choice if platform engineering is a strategic moat for your business. For everyone else, Ownkube is that architecture, turned into a product, on either on-ramp. Push straight to Ownkube Compute with no AWS account at all, or connect an AWS account and we bootstrap k3s (or EKS, at scale) and the supporting services. Either way you get the Heroku workflow (git push, managed Postgres, a ready-to-share HTTPS address, horizontal autoscaling) running on infrastructure that's yours. Ownkube covers the parts nobody enjoys: building the container, rolling it out behind a health check with a rollback if it fails, and keeping logs and metrics in one place. Pricing (as of August 2026): Ownkube Compute starts at [$5 a month](/pricing) on a prepaid wallet, no AWS account needed. BYOC Starter is free forever on one AWS instance (you pay AWS directly, roughly $30/month for a t3.medium); BYOC Production is $5 per vCPU plus $1 per GiB RAM per month, metered on what you request, and you pay AWS directly at wholesale. No markup on compute, no call-us-for-a-quote pages, and on BYOC every dollar of AWS Activate credit applies straight to wholesale EC2. If you've been sketching this architecture on a whiteboard for a quarter, or you're one Heroku invoice away from forcing the conversation with your CTO, [connect your AWS account](https://app.ownkube.io/login) and deploy your first app, or push straight to Ownkube Compute if you don't want to touch AWS yet. Fifteen minutes, and the infrastructure is yours on the other side of it. --- ## Connect your AWS account to Ownkube in two minutes > Connecting AWS to Ownkube is zero-input: no account ID to paste, no region to pick. The whole flow, plus where to find your AWS account ID. - Canonical: https://ownkube.io/blog/connect-your-aws-account - Markdown: https://ownkube.io/blog/connect-your-aws-account.md - Published: 2026-03-07 - Author: Abhishek Jaiswal - Category: How-To - Tags: aws, aws-account-id, getting-started, how-to, onboarding Your AWS account ID is a 12 digit number, something like `1234-5678-9012`. To find it: sign in to the AWS console, click your name in the top right corner, and read the number at the top of the menu that opens. No command line required. ## Find it in the console 1. Go to [console.aws.amazon.com](https://console.aws.amazon.com) and sign in. 2. Click your name in the **top right corner**. A menu drops down. 3. The **Account ID** sits at the top of that menu, with a copy button next to it. ![Illustration of the AWS console top navigation bar with the account menu open, showing the 12 digit account ID](/blog/connect-your-aws-account-console-menu.svg) A few things worth knowing: - It is **always 12 digits**. AWS shows it with dashes (`1234-5678-9012`) or without (`123456789012`). Same number. - It is **not a secret**. AWS does not treat account IDs as sensitive, so you can paste yours into a setup form without rotating anything. - It does **not change**, and it is the same for everyone in the account. If you do not see it (you might be signed in as an IAM user), open the search bar, type **IAM**, and the account ID shows on the IAM dashboard too. If you have more than one account, the number always reflects the one you are currently signed in to, so check the account name next to it. ## Connecting to Ownkube (you do not type it in) Here is the part that surprises people: **Ownkube never asks you to paste your account ID.** The connection is zero-input. You start it from Ownkube, approve a setup page in your own AWS account, and Ownkube detects the account automatically. Knowing your account ID is still handy for confirming you are connecting the right one, but you will not type it anywhere. The whole flow takes about two minutes and never touches a terminal. 1. **Start the connection in Ownkube.** There is nothing to fill in, no account ID and no region picker. Ownkube opens a CloudFormation setup page in your own AWS account in a new tab. 2. **Approve the grant in AWS.** That page creates a scoped role that lets Ownkube manage resources inside your account, on your behalf. There are only two things to do on it. First, tick the box that lets the template create the access role: ![AWS CloudFormation acknowledgment box, with an arrow pointing to the checkbox that permits creating the IAM access role](/blog/connect-your-aws-account-acknowledge-iam.png) Then click **Create stack**. That is the last click on your side: ![AWS CloudFormation action bar, with an arrow pointing to the orange Create stack button](/blog/connect-your-aws-account-create-stack.png) 3. **Leave the rest to Ownkube.** It detects the account the moment the stack finishes and verifies the connection, then you are ready to create your first cluster. You can revoke access anytime by deleting the stack from AWS. The key point: **Ownkube runs in your AWS account, not ours.** You pay AWS directly at wholesale rates, and your data and infrastructure stay under your control. ## Related reading - [Move the same app into your own AWS account](/blog/move-app-to-your-own-aws) - [A Heroku alternative in your own AWS account](/blog/heroku-alternative-in-your-own-aws-account) - [Migrate from Railway to your own AWS account](/blog/migrate-from-railway-to-your-own-aws) If you want to deploy something without standing up a platform team first, that is what Ownkube is built for. Start free on a single-node cluster and scale up when you are ready. See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login). --- ## Kubernetes events disappear after an hour > Kubernetes events vanish after 60 minutes. How to export them to Elasticsearch, Slack, Loki, and 30+ other destinations before they are gone. - Canonical: https://ownkube.io/blog/kubernetes-event-monitoring-complete-guide - Markdown: https://ownkube.io/blog/kubernetes-event-monitoring-complete-guide.md - Published: 2026-03-07 - Author: Abhishek Jaiswal - Category: Engineering - Tags: kubernetes, monitoring, observability, events, helm If you've ever run `kubectl get events` and wished those events didn't vanish after an hour, keep reading. Kubernetes events tell you exactly what's happening in your cluster (pod scheduling failures, image pull errors, OOM kills), but by default they disappear from etcd after 60 minutes. This guide walks through why that's a problem and how to export events to the tools your team already uses. ## What are Kubernetes events? Every time something happens in your cluster, Kubernetes creates an [**Event**](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/event-v1/) object. A pod gets scheduled, a container crashes, a volume fails to mount. Events are first-class API resources, same as Pods or Deployments. Here's what one looks like: ```yaml apiVersion: v1 kind: Event metadata: name: my-app-pod.17a6f8e2c namespace: production reason: BackOff message: "Back-off restarting failed container" type: Warning involvedObject: kind: Pod name: my-app-pod namespace: production count: 15 firstTimestamp: "2026-03-07T10:00:00Z" lastTimestamp: "2026-03-07T10:15:00Z" ``` The useful fields: - **Reason**: Machine-readable cause (`BackOff`, `FailedScheduling`, `Unhealthy`, `Pulling`, `Killing`) - **Type**: Either `Normal` or `Warning` - **Message**: Human-readable description of what happened - **InvolvedObject**: The resource this event is about - **Count**: How many times this event has occurred ## Why the defaults are a problem Three things make stock Kubernetes events nearly useless for debugging: ### Events expire after 1 hour The API server retains events for **60 minutes**. A pod crash at 2 AM is gone by 9 AM. You're left guessing. ### No search or aggregation `kubectl get events` gives you a flat, unsorted list. No cross-namespace search, no severity filtering, no time correlation. Just a wall of text. ### No alerting Kubernetes won't tell you when something goes wrong. A `FailedScheduling` event could fire hundreds of times before anyone notices, unless someone happens to be watching. ## Export events to external systems The fix is an event exporter: a lightweight component that watches the Kubernetes event stream via the API server and forwards events to external destinations (sinks) like [Elasticsearch](https://www.elastic.co/elasticsearch), Slack, [Loki](https://grafana.com/oss/loki/), [Kafka](https://kafka.apache.org), or webhooks. The pipeline: ![Kubernetes event exporter pipeline: the API server streams events to a SharedInformer-backed watcher, which passes them through routing rules that filter, match, and drop, then fans out to sinks like Slack, Elasticsearch, Loki, and Kafka/webhooks.](/diagrams/event-exporter-pipeline.png) The exporter uses a [SharedInformer](https://pkg.go.dev/k8s.io/client-go/tools/cache#SharedInformer) to watch events without polling, applies configurable routing rules, and delivers them to one or more sinks. ## Setting up the exporter The [Ownkube Kubernetes Events Exporter](https://github.com/ownkube/kubernetes-events-exporter) is a fork we maintain. The upstream project by Opsgenie (later Resmo) has been dormant since 2023 and has some bugs that cause silent event loss. More on that later. ### Install with [Helm](https://helm.sh) ```bash helm install event-exporter \ oci://ghcr.io/ownkube/charts/kubernetes-events-exporter \ --namespace monitoring \ --create-namespace ``` ### Basic configuration The exporter takes a YAML config (deployed as a ConfigMap). A minimal config that sends everything to stdout: ```yaml logLevel: info route: routes: - match: - receiver: "stdout" receivers: - name: "stdout" stdout: {} ``` ## Routing You can filter and direct events to different sinks based on any event property: namespace, event type, reason, involved object kind, etc. ### Route warning events to Slack ```yaml route: routes: - match: - receiver: "slack-warnings" match: kind: ".*" type: "Warning" - match: - receiver: "elasticsearch" receivers: - name: "slack-warnings" slack: token: "${SLACK_TOKEN}" channel: "#k8s-alerts" message: | *{{ .Reason }}* in {{ .InvolvedObject.Namespace }}/{{ .InvolvedObject.Name }} ```{{ .Message }}``` - name: "elasticsearch" elasticsearch: hosts: - "https://elasticsearch:9200" index: "k8s-events" ``` ### Drop noisy events Some events are just noise. Drop them before they hit any sink: ```yaml route: drop: - type: "Normal" reason: "LeaderElection" - namespace: "kube-system" reason: "ScalingReplicaSet" routes: - match: - receiver: "elasticsearch" ``` ### Route by namespace Production events go to PagerDuty. Staging events go to Slack. Everything goes to Elasticsearch. ```yaml route: routes: - match: - receiver: "pagerduty-webhook" match: namespace: "production" type: "Warning" - match: - receiver: "slack-staging" match: namespace: "staging" - match: - receiver: "elasticsearch" ``` ## Sink examples The exporter supports 30+ sinks. Here are the ones most people use. ### Elasticsearch Store all events for long-term analysis and Kibana dashboards: ```yaml receivers: - name: "elasticsearch" elasticsearch: hosts: - "https://elasticsearch.monitoring:9200" index: "k8s-events" username: "${ES_USER}" password: "${ES_PASSWORD}" useEventID: true ``` ### Loki Feed events into [Grafana Loki](https://grafana.com/oss/loki/) for log-style querying: ```yaml receivers: - name: "loki" loki: url: "http://loki.monitoring:3100/loki/api/v1/push" streamLabels: app: "kubernetes-events" basicAuth: username: "${LOKI_USER}" password: "${LOKI_PASSWORD}" ``` ### Slack ```yaml receivers: - name: "slack" slack: token: "${SLACK_TOKEN}" channel: "#k8s-events" message: | *{{ .Type }}* event on *{{ .InvolvedObject.Kind }}* `{{ .InvolvedObject.Name }}` Namespace: `{{ .InvolvedObject.Namespace }}` Reason: `{{ .Reason }}` Message: {{ .Message }} ``` ### Kafka ```yaml receivers: - name: "kafka" kafka: brokers: - "kafka-broker:9092" topic: "kubernetes-events" tls: enable: true ``` ### Webhook Send events to any HTTP endpoint: ```yaml receivers: - name: "webhook" webhook: endpoint: "https://your-api.example.com/k8s-events" headers: Authorization: "Bearer ${WEBHOOK_TOKEN}" Content-Type: "application/json" ``` ### Prometheus Turn events into [Prometheus](https://prometheus.io) metrics for alerting with [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/): ```yaml receivers: - name: "prometheus" prometheus: labels: - "reason" - "type" - "involvedObject.kind" - "involvedObject.namespace" ``` ## Events worth watching Not all events matter equally. These are the ones that usually point to real problems. ### Pod lifecycle issues | Reason | Type | What it means | |--------|------|---------------| | `BackOff` | Warning | Container keeps crashing and restarting | | `Unhealthy` | Warning | Liveness or readiness probe failed | | `OOMKilling` | Warning | Container exceeded memory limits | | `FailedMount` | Warning | Volume couldn't be mounted | | `ErrImagePull` | Warning | Can't pull the container image | ### Scheduling problems | Reason | Type | What it means | |--------|------|---------------| | `FailedScheduling` | Warning | No node has enough resources | | `Preempting` | Normal | Pod is being preempted for a higher-priority pod | | `NotTriggerScaleUp` | Warning | Cluster autoscaler can't add nodes | ### Node issues | Reason | Type | What it means | |--------|------|---------------| | `NodeNotReady` | Warning | Node is unhealthy | | `EvictionThresholdMet` | Warning | Node is running low on resources | | `Rebooted` | Warning | Node was rebooted | ## Practical tips ### Drop noise early Leader election events, successful image pulls, and routine scaling events generate a lot of volume. Drop them at the top of your routing tree. Your storage bill and your Slack channel will thank you. ### Don't hardcode secrets The exporter supports `${ENV_VAR}` syntax. Use it with Kubernetes Secrets: ```yaml env: - name: SLACK_TOKEN valueFrom: secretKeyRef: name: event-exporter-secrets key: slack-token ``` ### Separate sinks for separate jobs - Elasticsearch or Loki for historical analysis - Slack for real-time awareness of warnings - Webhook or PagerDuty for on-call alerting - Prometheus for dashboards and SLO tracking ### Run with leader election If you're running multiple replicas, enable leader election so only one instance processes events: ```yaml leaderElection: enabled: true leaderElectionID: "event-exporter-leader" ``` ### Monitor the exporter itself It exposes Prometheus metrics at `/metrics`. Set up alerts for high error rates on sink delivery, event processing lag, and exporter pod restarts. ## Why we maintain this fork The [original `kubernetes-event-exporter`](https://github.com/resmoio/kubernetes-event-exporter) by Opsgenie has been dormant since 2023. We ran into bugs in production and started fixing them. The fork now includes: - A fix for silent event loss. The upstream only handled add and delete operations, missing updates entirely. - Accurate event age calculation using `max(EventTime, FirstTimestamp, LastTimestamp)` instead of just `LastTimestamp`, which was causing stale events to get re-exported. - Loki improvements: basic auth, stream label templating, TLS transport fixes. - A Prometheus sink for converting events directly into metrics. - Elasticsearch v8 compatibility (the v8 API broke the upstream). - SNS FIFO support with proper message group IDs. The full code and Helm chart are on [GitHub](https://github.com/ownkube/kubernetes-events-exporter). ## Getting started 1. Install the exporter via Helm into your monitoring namespace 2. Start with stdout + one sink (Slack or Elasticsearch) to validate routing works 3. Add drop rules for the noisy stuff 4. Expand sinks as you need them That's it. Kubernetes events go from "gone in 60 minutes" to searchable, filterable, and alertable. ## If you'd rather skip the plumbing If you're running k3s or EKS in your own AWS account, [Ownkube](https://ownkube.io) wires event routing in by default: warnings into Slack, everything into your logging backend, crashloops explained in plain English, without installing a chart or writing a routing YAML. It runs on your cluster in your AWS account, so the events never leave your perimeter. ## Related reading - [Kubernetes cost optimization for startups](/blog/kubernetes-cost-optimization-startups) - [EKS vs k3s on AWS: cost and when to choose](/blog/eks-vs-k3s-on-aws-for-startups) - [Ingress-NGINX EOL: migrating to Gateway API](/blog/ingress-nginx-eol-gateway-api-migration) See [Ownkube pricing](/pricing) for the per-minute wallet model, from $5 a month. [Deploy your first app](https://app.ownkube.io/login) and event monitoring is one of the things you stop thinking about.