Environment variables are how you inject configuration into your Cloud Functions without hardcoding secrets or environment-specific values directly into your code. They’re like little key-value pairs that your function can read at runtime.
Let’s see this in action. Imagine you have a Cloud Function that needs to connect to a PostgreSQL database. You wouldn’t want to put your DB_PASSWORD directly in your index.js file. Instead, you’d set it as an environment variable.
Here’s a simple index.js that uses an environment variable:
const express = require('express');
const app = express();
// Read the database URL from an environment variable
const dbUrl = process.env.DATABASE_URL;
app.get('/', (req, res) => {
if (!dbUrl) {
res.status(500).send('DATABASE_URL environment variable not set!');
return;
}
// In a real scenario, you'd use dbUrl to connect to your database
res.status(200).send(`Database connection string is: ${dbUrl.substring(0, dbUrl.indexOf(':', dbUrl.indexOf('://') + 3) + 1)}...`);
});
exports.myFunction = functions.https.onRequest(app);
When you deploy this function, you’d set DATABASE_URL to something like postgres://user:password@host:port/database. Your function then reads process.env.DATABASE_URL to get this value.
The core problem environment variables solve is decoupling configuration from code. This means:
- Security: You can store sensitive information (API keys, database credentials) without exposing them in your source control.
- Flexibility: You can deploy the exact same code to different environments (development, staging, production) by simply changing the environment variables.
- Maintainability: Updating a configuration value doesn’t require a code change and redeployment. You just update the variable.
Internally, when Cloud Functions starts an instance of your function, it injects these environment variables into the execution environment. The Node.js process.env object is populated with these values, making them accessible throughout your code.
You manage these variables in a few ways, depending on your deployment method:
-
gcloudCLI: When deploying or updating a function, you can specify environment variables using the--set-env-varsflag. For example:gcloud functions deploy myFunction \ --runtime nodejs18 \ --trigger-http \ --set-env-vars DATABASE_URL=postgres://user:pass@host:5432/db,API_KEY=abcdef12345This command sets two environment variables:
DATABASE_URLandAPI_KEY. You can list multiple variables separated by commas. -
firebase.json(for Firebase Functions): You can define environment variables within yourfirebase.jsonfile under thefunctions.environmentVariablessection.{ "functions": { "environmentVariables": { "DATABASE_URL": "postgres://user:pass@host:5432/db", "API_KEY": "abcdef12345" } } }When you run
firebase deploy --only functions, these variables are applied. -
Google Cloud Console: You can also set and manage environment variables directly through the Cloud Functions UI in the Google Cloud Console. Navigate to your function, click "Edit," and find the "Runtime, build, and connections" section where you can add or modify variables.
The most surprising thing about environment variables, especially when you’re used to local development, is that they are scoped to the function version. If you have multiple versions of the same function deployed (e.g., a stable and a beta version), each can have its own distinct set of environment variables. This is crucial for gradual rollouts and A/B testing configurations. You don’t update the variable globally; you update it for a specific function deployment.
When you update an environment variable for a deployed function, Cloud Functions will trigger a new deployment for that function. Existing instances will continue to run with the old variables until they are scaled down, while new instances will be started with the updated variables. This rolling update ensures minimal disruption.
The next challenge you’ll face is managing secrets. While environment variables are great for configuration, storing highly sensitive data like private keys directly in firebase.json or passing them via --set-env-vars can still be risky if your source control isn’t locked down. This is where Secret Manager integration comes in.