Skip to main content
Environment variables are the standard way to pass configuration and secrets (database credentials, API keys, feature flags) to your app without hardcoding them.
Running a 1-Click App? Use instapods env set - it writes to the app’s own config file and restarts the app automatically. The .env-in-app-directory approach below is for custom-code pods (Static, PHP, Node.js, Python, Go).

Setting Environment Variables

Set one or more variables straight from the CLI:
List or remove them later (values are masked unless you pass --show-values):
For custom-code pods this writes to /home/instapod/app/.env and restarts the app service. For 1-Click Apps it targets the app’s own config file and restarts the app. Either way you do not need to run reload or restart afterwards.
Setting a variable does not put it in your app’s process environment. It writes a line into a .env file, and your app has to read that file. If instapods env list shows the variable but your app still says it is unset, that is the reason - see How your app reads them below.

Option 2: Create a .env File

The manual approach — create a .env file in your app directory via the CLI:
Nothing restarts your app when you write the file this way, so reload afterwards:
Option 1 is still the better choice for anything with a #, a quote or a newline in it: env set quotes the value so it reads back exactly as typed, and a heredoc does not.

Option 3: Set at Pod Creation (API)

Pass environment variables when creating a pod via the API:
Variables set this way are written to /etc/environment, which login shells read. Systemd does not read /etc/environment, so your app service does not inherit them. For anything your app needs to see, use instapods env set (Option 1) — those land in .env and reach your app.

How your app reads them

instapods env set writes KEY=value into /home/instapod/app/.env and restarts your app. Your app then gets those values two ways, and either one is enough:
  1. We put them in the process environment for you. The app’s systemd unit reads a copy of your .env that InstaPods generates, so process.env.API_KEY / os.environ["API_KEY"] / os.Getenv("API_KEY") work with no library and no code.
  2. Your app reads the file itself, with dotenv, load_dotenv(), godotenv.Load(), or the support already built into Laravel, Symfony, Next and Vite.
If your app does both, they agree - a dotenv loader never overwrites a variable that is already set, and we generate our copy with the same parser your loader uses, so the value is the same either way.

Three cases where you still need to read the file yourself

The generated copy is deliberately conservative. It leaves a variable out rather than risk handing your app a value that is subtly different from what you typed. A variable is skipped when:
  • It is one we manage. PORT, HOST, HOSTNAME and NODE_ENV are set by InstaPods so the proxy can reach your app. Setting them yourself is ignored on purpose - if it were not, your app would bind somewhere we do not route to and stop serving traffic.
  • Its value contains both an apostrophe and a newline (or a carriage return). That combination is the one shape we cannot guarantee systemd reads identically to dotenv, so we leave it out rather than risk corrupting it. A multi-line key or JSON credential with no apostrophe is passed through fine.
  • You edited .env by hand - over SSH, in the Web IDE, or with instapods exec. We cannot tell what changed, so we switch the generated copy off for that pod until your next env set or dashboard save. Your app keeps working; it just has to read the file itself until then.
In all three cases the value is still in /home/instapod/app/.env, so a dotenv.config() or load_dotenv() in your app covers you. Adding one is never wrong. The rest of this section shows that code for each stack.

Node.js

The systemd service sets NODE_ENV=production by default. That one comes from the unit itself, and a value you set for it in .env is ignored. process.env.DATABASE_URL already works with no library. Use the dotenv package if you also want the file read directly (see the three cases above):
On Node 20 and newer you can skip the package and start your app with the flag instead:
If your pod was created before mid-2026, some values may still live in /home/instapod/app/.env.local from an older layout. instapods env set keeps both files in step for the keys you set, so a stale .env.local cannot shadow them at build time. New keys go to .env.

Python

Use python-dotenv:

PHP

PHP-FPM provides access to system environment variables via getenv(). For .env files, parse them manually or use a library:
For Laravel and Symfony the framework reads .env for you, so there is nothing to add.
PHP pods have no app service, so instapods env set has nothing to restart - PHP re-reads the file on the next request. The exception is a cached Laravel config: if you have run php artisan config:cache, run it again after changing a variable, or the cached values keep winning.

Go

Use godotenv:

Static

Static sites don’t run server-side code. For JavaScript apps that need configuration, use a config.js file:

Common Pattern: Database Credentials

After installing a database service, get the credentials and set them as env vars:

Variables InstaPods sets itself

Four keys belong to the platform, and a value you set for them is not applied: The write still goes through - the file is yours - but instapods env set and the dashboard both warn you at the moment you save, rather than leaving you to discover it as a 502 later:
Over the API, the same text comes back in a warnings array on the response.
Read the port from the environment with a fallback (process.env.PORT || 3000) and let InstaPods supply it. If your app must listen somewhere specific, change the port your app is proxied on instead - the Deploy Doctor’s wrong port fix does exactly that.

File permissions

.env holds secrets, so it is not readable by other accounts on the pod: InstaPods keeps it at mode 600, owned by instapod. On a PHP pod it is 640 and group www-data, because php-fpm reads the file as that group. If you create the file yourself over SSH, set the mode yourself - chmod 600 .env. InstaPods re-applies the safe mode on every env set, so the simplest way to fix a file you wrote by hand is to set one variable through the CLI.

Updating Variables

instapods env set merges: the keys you pass are updated or added, everything else in the file is left alone, and comments and blank lines survive.
Both restart the app service for you. If you edited /home/instapod/app/.env by hand instead - over SSH, in the Web IDE, or with instapods exec - nothing restarts your app, so run instapods pods reload my-app yourself. See Deploy, Reload, Restart. A hand edit also switches off the copy we put in the process environment for that pod, because we cannot tell what you changed. Your app has to read .env itself until your next instapods env set or dashboard save, which regenerates it.
The .env file is stored inside your pod. If you delete the pod, the file is lost. Keep a copy of your environment variables in a secure location.
Multi-line values - a private key, a service-account JSON - are supported. Quote the value in your shell and instapods env set stores it whole, newlines and all, so it reads back exactly as you typed it:

Best Practices

  • Never commit secrets to git — always use .env files or environment variables
  • Use defaults in code — Always provide fallback values (process.env.PORT || 3000)
  • Load the file anyway — Your app gets the variables from the process environment without any code, but a load_dotenv() / dotenv.config() also covers the three cases above where we deliberately leave a variable out of the generated copy. It costs one line and never hurts
  • Prefer instapods env set over editing the file — it restarts the app for you and quotes values that contain #, quotes or newlines so they read back exactly as typed
  • Separate configs — Use different .env files for development vs production