Auto-detect deploys
Auto-detect is the “just connect a repo, pick nothing” path. You point PandaStack at a Git repository, choose nothing else, and it decides whether your project should be a static site (published to the CDN with no pod) or a container app (a Kubernetes pod). The goal is that the first build always works.
At create time the deploy type is auto. After classification it persists as either static or container — auto is never a lasting type.
How it works
Section titled “How it works”Auto-detect combines two mechanisms:
- A pre-filter classifier (
classify-repo) inspects the repository and decides whether it is a pure static SPA or needs a container. - Railpack buildpacks handle the actual container build with
language="auto"— zero-config framework and runtime detection — so even when a container is chosen, no build or start command is required. See Build & buildpacks.
If the classifier proves the repo is a pure client-side app, the project is persisted as static and its build output is published to the CDN. Otherwise it is persisted as container and built via Railpack.
The classifier is an asymmetric-risk whitelist
Section titled “The classifier is an asymmetric-risk whitelist”The classifier returns static only on positive proof of a known pure-client framework with a deterministic output directory and zero server signals. Everything else falls back to container.
This asymmetry is deliberate:
- A false
staticwould ship a broken or empty CDN publish (there is no server to run the app) — a visible, user-facing failure. - A false
containermerely leaves an SPA running in a pod that a CDN could have served — it wastes a pod but the site still works.
So container is the safe error. Any failure during classification — a private repo without a token, a GitHub rate limit, an unparseable package.json — falls back to container.
What forces a container
Section titled “What forces a container”The classifier forces container when it sees any of these signals:
| Signal | Examples |
|---|---|
| Server frameworks | Express, Koa, Fastify, NestJS, Hono, Strapi, and similar |
| SSR / ambiguous meta-frameworks | Next.js, Nuxt, SvelteKit, Remix, React Router 7, SolidStart, Qwik City, Astro with an SSR adapter, Angular SSR, Gatsby with an adapter |
| Library / publishable-package shape | a files[] field, main pointing into dist, or a bin entry |
| Monorepo / workspace roots | workspace configuration at the repo root |
| Server-intent token in a deploy-relevant script | a server-intent token in start/build hooks (never in dev/preview scripts) |
Missing or unparseable package.json | can’t prove it’s static |
A root Dockerfile | (with one exception — see below) |
What proves static
Section titled “What proves static”The classifier maps a small set of known pure-client frameworks to their deterministic build output directory:
| Framework | Detected by | Build output dir |
|---|---|---|
| Create React App | react-scripts | build |
| Vue CLI | @vue/cli-service | dist |
| Vite SPA | a UI dependency and an index.html present | dist |
| Gatsby | Gatsby with no SSR adapter | public |
| Astro | Astro with no SSR adapter | dist |
Angular stays a container. Its output directory is dist/<project>/browser, which cannot be resolved reliably at classify time, so Angular is not demoted to static.
The Dockerfile demotion exception
Section titled “The Dockerfile demotion exception”A root Dockerfile normally forces container. There is one exception: if the repo is a proven-static SPA whose Dockerfile is a provably-trivial dev-server one (FROM node, npm install, CMD npm run start), the project is demoted to static. This override can only demote (container → static); it can never promote a container to static without independent proof.
Smart defaults for paid organizations
Section titled “Smart defaults for paid organizations”When a paid organization uses auto-detect and the repo lands on the container path, PandaStack applies production-friendly defaults:
- Standard tier compute (1 vCPU / 2 GB) instead of the free tier
- HPA autoscaling enabled, so the app is always-warm rather than scaling to zero
Both of these require an active paid plan. A free organization always lands on the free-tier defaults (free compute + KEDA scale-to-zero). The create path checks isOrgPaid, so a raw API caller cannot sneak a free org onto a paid tier or HPA.
Overriding the decision
Section titled “Overriding the decision”You do not have to accept auto-detect. Under Advanced on the create form you can:
- Force the deploy type to
staticorcontainerexplicitly instead ofauto - Set the language for a container:
nodejs,python,go, ordocker(use the repo’s own Dockerfile) instead ofauto(Railpack) - Provide explicit build and start commands
- Pick a compute tier and enable or disable autoscaling (paid only)
If you set an explicit container language, per-language required fields apply:
| Language | Required for explicit container |
|---|---|
auto (Railpack) | nothing — buildpacks derive everything |
nodejs | a start command |
python | build and start commands |
go | build and start commands |
docker | uses the repo’s own Dockerfile |
Static projects are also exempt from command requirements — the framework defaults figure it out.