Back to Blog
Tutorial9 min read2026-07-31

Environment Variable Prompting with pandastack.json

Stop deploying apps with missing configuration—use the env array in pandastack.json to prompt users for API keys and secrets before the build starts.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Most app deploys fail not because the code is broken, but because environment variables are missing or misconfigured. The app builds successfully, starts, then crashes with "DATABASE_URL is not defined" or "STRIPE_KEY is required". The deploy page shows green checkmarks, but the app is unusable.

The env array in pandastack.json fixes this by making the deploy page prompt for required variables before starting the build. Users see input fields with descriptions, fill them in, and the values are injected at build time or runtime depending on the app type. This is the same pattern Vercel uses, and it prevents the entire class of "forgot to set an env var" failures.

The problem: silent configuration failures

Without prompting, deploying an app that needs environment variables looks like this:

  1. 1User clicks the deploy button
  2. 2Build starts immediately
  3. 3Build succeeds (because the code compiles)
  4. 4App starts, reads process.env.API_KEY, gets undefined
  5. 5App crashes or serves errors

The user sees "Deployment succeeded" in the dashboard, but the app is broken. They have to figure out which variables are needed, navigate to the project settings, add them, and redeploy. This adds 5+ minutes to the onboarding flow and loses users who don't read the docs.

The solution: env array in pandastack.json

Add an env array to pandastack.json:

{
  "type": "container",
  "startCommand": "node server.js",
  "env": [
    {
      "key": "DATABASE_URL",
      "description": "PostgreSQL connection string from the Databases tab"
    },
    {
      "key": "STRIPE_SECRET_KEY",
      "description": "Stripe API secret key (starts with sk_)"
    },
    {
      "key": "NODE_ENV",
      "description": "Node environment (development or production)",
      "value": "production"
    }
  ]
}

When someone deploys this app, the deploy page shows three input fields:

  • DATABASE_URL: empty, required
  • STRIPE_SECRET_KEY: empty, required
  • NODE_ENV: pre-filled with "production", editable

The user fills in the first two, confirms, and the deploy starts with those values injected. The app boots with valid configuration, and the first deploy succeeds.

When to use this

Every app that reads environment variables should use the env array. Specifically:

  • API keys: Stripe, Sendgrid, AWS credentials
  • Database URLs: PostgreSQL, MySQL, Redis connection strings
  • Service URLs: microservice endpoints, third-party APIs
  • Feature flags: NODE_ENV, DEBUG, LOG_LEVEL

Even if you're deploying your own app (not a public template), adding the env array makes redeploys faster—you don't have to remember which vars are needed or copy-paste them from the dashboard.

The two forms: string or object

The simplest form is a string:

{
  "env": ["DATABASE_URL", "STRIPE_KEY", "NODE_ENV"]
}

This tells the deploy page to prompt for these three variables with no description or default. It's faster to write but less helpful to users.

The object form adds a description and optional default:

{
  "env": [
    {
      "key": "DATABASE_URL",
      "description": "PostgreSQL connection string"
    },
    {
      "key": "PORT",
      "description": "Server port (default: 3000)",
      "value": "3000"
    }
  ]
}

The description appears as help text below the input field. The value pre-fills the input—users can override it if needed.

Pre-filling with safe defaults

For variables that have sensible defaults (like NODE_ENV=production or PORT=3000), pre-fill them:

{
  "key": "NODE_ENV",
  "description": "Node environment",
  "value": "production"
}

The user can change it if they want a different value, but most won't. This reduces the number of fields they have to fill in.

Don't pre-fill secrets. If you add "value": "sk_test_12345" to STRIPE_SECRET_KEY, users will deploy with your test key, which is confusing and potentially insecure. Leave secrets empty and let the user fill them in.

Static vs container: build-time vs runtime

For static sites (Vite, Next.js export, Astro), env vars are injected at build time:

{
  "type": "static",
  "buildCommand": "npm run build",
  "outputDir": "dist",
  "env": [
    { "key": "VITE_API_URL", "description": "Backend API endpoint" }
  ]
}

When the build runs, VITE_API_URL is available as process.env.VITE_API_URL. Vite inlines it into the bundled JavaScript. The final static site has the value baked in—it can't be changed without a rebuild.

For container apps (Express, FastAPI, Go), env vars are injected at runtime:

{
  "type": "container",
  "startCommand": "node server.js",
  "env": [
    { "key": "DATABASE_URL", "description": "Postgres connection string" }
  ]
}

The app reads process.env.DATABASE_URL when it starts. You can update the value in the dashboard without rebuilding—just restart the app.

Linking a managed database

If your app needs a managed PostgreSQL database, the env array should prompt for DATABASE_URL:

{
  "env": [
    {
      "key": "DATABASE_URL",
      "description": "Create a database in the Databases tab and paste the connection string here"
    }
  ]
}

Alternatively, instruct users to link the database in the dashboard after deploying. But prompting for it upfront is clearer—the user knows they need a database before starting the deploy.

PandaStack doesn't auto-provision databases from pandastack.json (yet). Users create the database separately and paste the connection string into the deploy form.

Multi-environment setups

If you deploy the same app to staging and production, the env array ensures both environments prompt for the same variables. The user fills in different values for each deploy:

Staging:

API_URL=https://api-staging.example.com
STRIPE_KEY=sk_test_12345

Production:

API_URL=https://api.example.com
STRIPE_KEY=sk_live_67890

The pandastack.json file is the same in both cases—only the values differ.

The deploy button with env params

You can pre-seed env var names via query params in the deploy button URL:

[![Deploy](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=acme/app&env=DATABASE_URL,STRIPE_KEY)

The deploy page shows input fields for DATABASE_URL and STRIPE_KEY. But if the repo has a pandastack.json with an env array, the file's values take precedence. The query param is a fallback for repos without the file.

CLI and API workflows

The panda CLI doesn't interactively prompt for env vars (yet). You pass them as flags or environment variables:

panda projects create \
  --name myapp \
  --repo acme/app \
  --env DATABASE_URL=postgres://... \
  --env STRIPE_KEY=sk_live_...

Or set them after creating the project:

panda projects env set 42 DATABASE_URL postgres://...

The REST API uses a JSON payload:

curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer psk_live_your_token" \
  -d '{
    "name": "myapp",
    "repositoryName": "acme/app",
    "env": [
      { "name": "DATABASE_URL", "value": "postgres://..." },
      { "name": "STRIPE_KEY", "value": "sk_live_..." }
    ]
  }'

Both approaches bypass the prompting—you're responsible for providing the values.

Why this matters

Vercel pioneered the env var prompting pattern with their deploy button, and it dramatically improved the one-click deploy experience. Before that, deploying a template meant cloning the repo, reading the README, setting 10 env vars manually, then clicking deploy. With prompting, you fill in 3 fields and the deploy succeeds on the first try.

PandaStack's implementation is Vercel-compatible—if a repo has a Vercel-style config with an env array, you can swap the deploy button domain and it works. This makes migrating templates from Vercel to PandaStack trivial.

Common issues

Env var not injected: For static sites, verify the var name matches the framework's prefix (VITE_, NEXT_PUBLIC_, PUBLIC_ for Astro). For container apps, check that the app reads process.env.VAR_NAME, not a hardcoded value.

Prompt doesn't show: Make sure pandastack.json is at the repo root, not in a subdirectory. The deploy page fetches it from https://api.github.com/repos/owner/repo/contents/pandastack.json.

Value doesn't update after redeploy: For container apps, env vars are runtime config—you can change them in the dashboard and restart the app. For static sites, you have to rebuild.

Full example: Express app with secrets

pandastack.json:

{
  "type": "container",
  "language": "nodejs",
  "startCommand": "node server.js",
  "healthCheckPath": "/health",
  "env": [
    {
      "key": "DATABASE_URL",
      "description": "PostgreSQL connection string (postgres://user:pass@host/db)"
    },
    {
      "key": "JWT_SECRET",
      "description": "Random string for signing JWTs (generate with openssl rand -hex 32)"
    },
    {
      "key": "NODE_ENV",
      "description": "Node environment",
      "value": "production"
    }
  ]
}

README.md:

[![Deploy](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=acme/app)

Click to deploy. You'll be prompted for DATABASE_URL and JWT_SECRET.

Users click the button, fill in two fields, and the app deploys with working configuration.

References

  • [PandaStack Environment Variables Documentation](https://docs.pandastack.io/projects/env)
  • [Vercel Environment Variables](https://vercel.com/docs/projects/environment-variables)
  • [Vite Environment Variables](https://vitejs.dev/guide/env-and-mode.html)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also