Skip to content

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 containerauto is never a lasting type.

Auto-detect combines two mechanisms:

  1. A pre-filter classifier (classify-repo) inspects the repository and decides whether it is a pure static SPA or needs a container.
  2. 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 static would ship a broken or empty CDN publish (there is no server to run the app) — a visible, user-facing failure.
  • A false container merely 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.

The classifier forces container when it sees any of these signals:

SignalExamples
Server frameworksExpress, Koa, Fastify, NestJS, Hono, Strapi, and similar
SSR / ambiguous meta-frameworksNext.js, Nuxt, SvelteKit, Remix, React Router 7, SolidStart, Qwik City, Astro with an SSR adapter, Angular SSR, Gatsby with an adapter
Library / publishable-package shapea files[] field, main pointing into dist, or a bin entry
Monorepo / workspace rootsworkspace configuration at the repo root
Server-intent token in a deploy-relevant scripta server-intent token in start/build hooks (never in dev/preview scripts)
Missing or unparseable package.jsoncan’t prove it’s static
A root Dockerfile(with one exception — see below)

The classifier maps a small set of known pure-client frameworks to their deterministic build output directory:

FrameworkDetected byBuild output dir
Create React Appreact-scriptsbuild
Vue CLI@vue/cli-servicedist
Vite SPAa UI dependency and an index.html presentdist
GatsbyGatsby with no SSR adapterpublic
AstroAstro with no SSR adapterdist

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.

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.

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.

You do not have to accept auto-detect. Under Advanced on the create form you can:

  • Force the deploy type to static or container explicitly instead of auto
  • Set the language for a container: nodejs, python, go, or docker (use the repo’s own Dockerfile) instead of auto (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:

LanguageRequired for explicit container
auto (Railpack)nothing — buildpacks derive everything
nodejsa start command
pythonbuild and start commands
gobuild and start commands
dockeruses the repo’s own Dockerfile

Static projects are also exempt from command requirements — the framework defaults figure it out.