Skip to content

Container apps

A container project runs your code as a Kubernetes pod fronted by the PandaStack gateway. Use this type for anything with a server process: HTTP APIs, SSR web apps (Next.js, Nuxt, SvelteKit), background-serving workers, or any app that listens on a port. Containers are built with Railpack or a Dockerfile and reached at https://{name}.pandastack.app (or a custom domain).

At deploy time the worker creates a gateway service in front of your pod. The service is named:

org-{orgId}.custom-{name}.80

This name is created by the worker at deploy time — it is not stored on the project row. In the routing manifest, a container project has a single catch-all function route targeting:

kong://org-{orgId}.custom-{name}.80

Every request that isn’t a redirect, rewrite, or header rule is dispatched to your pod through this route. Because a container has a real server, its redirects are issued as 301 (via a Kong pre-function Lua handler), whereas static redirects are 302.

This is the one thing a container app must get right. PandaStack routes traffic to your app on a single port, and it tells your app which one through the PORT environment variable.

Two rules:

  1. Listen on the port from PORT — not a hardcoded number. PandaStack sets PORT for you (default 8080), and your app must bind to it.
  2. Bind to 0.0.0.0, not localhost/127.0.0.1. An app that listens only on the loopback address gets no traffic from the gateway. PandaStack also sets HOST=0.0.0.0 and HOSTNAME=0.0.0.0 — most frameworks (Express, Next.js, Nuxt, Vite, Flask, FastAPI, …) read these automatically, so you often don’t have to do anything.
// Node — read the port PandaStack gives you; fall back for local dev.
const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0");
# Python
port = int(os.environ.get("PORT", 3000))
app.run(host="0.0.0.0", port=port)

Some apps listen on a fixed port (say 18000) and ignore PORT. If you can’t change the code to read PORT, tell PandaStack which port your app uses by adding a PORT environment variable on the project that matches it — e.g. PORT=18000. PandaStack will then route to and health-check that port, and the deploy will succeed.

You choose how the container is built by setting the language on the create form:

LanguageBuild methodRequired fields
autoRailpack zero-config buildpacksnone
nodejsPlatform-generated Dockerfilea start command
pythonPlatform-generated Dockerfilebuild and start commands
goPlatform-generated Dockerfilebuild and start commands
dockerThe repo’s own Dockerfile

auto is the default for auto-detected and one-click deploys — Railpack detects the framework and derives the build and start commands, so no commands are required. See Build & buildpacks for the full framework and language matrix, Node version selection, Dockerfile override, and build caching.

Containers run on a fixed catalog of compute tiers. The free tier is the default; paid tiers require an active paid plan (the create path checks isOrgPaid, so a raw API caller cannot put a free org on a paid tier).

Tier slugLabelvCPURAM (GB)$/moFree?
free-tierFree0.250.50yes
standard-tierStandard1218.25no
c1-compute-tierC1-Compute2451.10no
c1-2xcompute-tierC1-2XCompute48102.20no
c2-compute-tierC2-Compute612109.50no
c2-2xcompute-tierC2-2XCompute816219.00no
m1-memory-tierM1-Memory1443.80no
m1-2xmemory-tierM1-2XMemory2887.60no
m2-memory-tierM2-Memory21294.90no
m2-2xmemory-tierM2-2XMemory424189.80no

Full tier details and pricing are on Instance tiers.

How a container scales depends on the plan:

  • Free tier — the app uses KEDA scale-to-zero. It runs a single replica that scales down to zero after idle and cold-starts on the next request (a few seconds of latency on the first hit).
  • Paid tiers — the app can enable HPA autoscaling, which keeps it always-warm and adds replicas under load.

Scale-to-zero and HPA autoscaling are mutually exclusive: with autoscaling on, the app is always-warm and never sleeps. Both a non-free tier and HPA require an active paid plan. See Autoscaling & scale-to-zero for min/max replicas, target CPU, and cold-start behavior.

A container can attach a persistent volume. The disk field is a size in GB (a PVC); 0 means no persistent volume. Changing compute — the tier, autoscaling, or disk — triggers a redeploy: the worker re-renders the Helm chart and rolls out the change.

Your pod is fronted by the gateway service and probed by Kubernetes. Expose a route that returns success when the app is ready so the platform knows when to send traffic. The healthCheckPath field (also settable via pandastack.json) tells PandaStack which path to probe.

A container gets https://{name}.pandastack.app and can attach a custom domain (paid). It supports redirects, rewrites, and response headers — for containers these also sync to Kong on the data plane. Analytics and metrics are captured at the gateway.

Container-hours are metered and billed on paid plans (pro/premium); free-tier containers cost $0 because they scale to zero. See Usage & metering. Free organizations may run up to 5 containers; paid plans are unlimited — see Plans & limits.