Adding New Variables to .env
File
You will need to add new secrets or variables to your .env
file. Here’s a simple guide to walk you through the process.
Managing Environment Variables
Handling environment variables is crucial for configuring your project across different stages, from local development to deployment in the cloud. In this guide, we will cover three types of .env
files commonly used in our project:
- Root
.env
for localdevelopment
. - CDK
.env
for initial deployment with AWS CDK (staging
,production
). - CI/CD
env
section for handling secrets securely during automated deployments (staging
,production
).
We’ll explore their use cases, configurations, and best practices for secure handling.
1. Root .env
– For Local Development
The root .env
file is used exclusively for local development. It contains environment-specific variables like API keys, database connection strings, or any other configuration needed to run the app locally.
Usage in config.ts
For example, if you want to add a new variable OAUTH_API_KEY
to your .env
file and test it locally first, you'll need to include it in your root .env
. After adding it, make sure to access the variable by including it in the configuration file (src/config/config.ts
).
Here's an example using Joi
for validation:
.when(Joi.object({ NODE_ENV: Joi.string().valid('development') }).unknown(), {
OAUTH_API_KEY: Joi.string().required().description('OAUTH_API_KEY'),
})
Then add it to the interface
interface Config {
...
OAUTH_API_KEY: string;
}
Finally, in const config
const config: Config = {
OAUTH_API_KEY: envVars.OAUTH_API_KEY,
}
By doing this, you ensure that the required environment variables are in place before the app starts, reducing potential runtime errors. You can now use OAUTH_API_KEY
as config.OAUTH_API_KEY
anywhere in the app.
2. CDK .env
– For AWS CDK Deployment
The CDK .env
file is used only once during the bootstrapping and deployment stages with AWS CDK. This is where you provide configurations required for setting up your cloud infrastructure.
Security Note
For sensitive variables like DATABASE_URL
, it’s best to use a placeholder (e.g., postgresql://) during bootstrapping and deployment with AWS CDK. Don't worry, the real value will be securely passed through GitHub CI/CD to reduce security risks, as hardcoding the actual DATABASE_URL
in the .env
can still pose vulnerabilities if not handled cautiously.
3. CI/CD .env
– For Secure Deployment
The CI/CD .env
handles environment variables securely during automated deployments, typically configured in a file like main.yml
for GitHub Actions. This file ensures secrets are not exposed in the deployment process.
How to Securely Pass New Environment Variables
-
Add a new variable in
cdk/.env
with a value for both the production and staging sections. i.e.OAUTH_API_KEY=...
-
Add sensitive data to GitHub Secrets. i.e.
STAGING_OAUTH_API_KEY
,PROD_OAUTH_API_KEY
-
Configure
.github/workflows/main.yml
to read secrets during the deployment:- name: CDK Deployment env: ... OAUTH_API_KEY: ${{ secrets.STAGING_OAUTH_API_KEY }}
Don't forget to add it for the
Prod
section as well- name: CDK Deployment env: ... OAUTH_API_KEY: ${{ secrets.PROD_OAUTH_API_KEY }}
Also, add it under
test
section:test: name: Run Tests if: github.ref == 'refs/heads/staging' uses: ./.github/workflows/ssm-docker-test.yml secrets: ... OAUTH_API_KEY: ${{ secrets.STAGING_OAUTH_API_KEY }}
-
Configure
.github/workflows/ssm-docker-test.yml
and add it underworkflow_call
:on: workflow_call: secrets: ... OAUTH_API_KEY: required: true
Also, pass it here:
- name: Build and test with Docker Compose (using host network) run: docker compose -f docker-compose.test.yml up --build --abort-on-container-exit env: ... OAUTH_API_KEY: ${{ secrets.OAUTH_API_KEY }}
-
Pass it in
cdk/bin/cdk.ts
.new CdkStack(app, 'CdkStack', { oAuthApiKey: process.env.OAUTH_API_KEY, }
-
Update interface in
cdk/lib/cdk-stack.ts
:export interface CdkStackProps extends StackProps { oAuthApiKey: string; }
-
Provide the values to the ECS stack:
new ECSStack(this, `ECS`, { oAuthApiKey: props.oAuthApiKey, ... }
-
Configure in
cdk/lib/nested/ecs-stack.ts
:export interface ECSStackProps extends NestedStackProps { oAuthApiKey: string; }
const { ... oAuthApiKey, } = props;
const container = taskDefinition.addContainer(`AppContainer`, { environment: { ... OAUTH_API_KEY: oAuthApiKey, }, });
⚠️ Adding environment variables to taskDefinition.addContainer
is crucial, as it ensures they are passed to your backend.
-
Update
src/config/config.ts
to validate variables for staging and production:.when(Joi.object({ NODE_ENV: Joi.string().valid('production', 'staging') }).unknown(), { then: Joi.object({ OAUTH_API_KEY: Joi.string().required().description('OAUTH_API_KEY'), }), })
-
Lastly, we need to pass this new env to run test. Go to
docker-compose.test.yml
and add your new variable.environment: ... OAUTH_API_KEY: ${OAUTH_API_KEY}
Summary
Managing environment variables effectively ensures a secure and maintainable development workflow. Here’s a quick recap:
- Root
.env
: For local development, validated insrc/config/config.ts
. - CDK
.env
: Used for bootstrapping and deploying with AWS CDK. Avoid hardcoding sensitive data, as subsequent deployments with CI/CD can manage it more securely. - CI/CD
env
: Pass secrets securely during automated deployments through GitHub Actions. Also validated insrc/config/config.ts
.
By following these practices, you can handle environment variables across multiple stages while minimizing security risks and maintaining consistency.