API operations · secrets management · environment variables · security · devops · indie developers
Managing API Keys and Secrets as a Small Team: A Practical Guide
A no-fluff guide to environment variables, .env files, and secrets management for indie developers and small API teams who need security without overengineering.
Published:
The Problem Most Small Teams Ignore Until It Hurts
Hardcoded API keys in source code, .env files accidentally committed to GitHub, credentials leaked through logs — these are not hypothetical risks. The 2024 GitGuardian State of Secrets Sprawl report found over 12.8 million secrets committed to public GitHub repositories in a single year. The real number across private repos and other platforms is orders of magnitude higher.
For indie developers and small software teams building with APIs, the temptation is to take shortcuts. You have one server, two developers, and a deadline. Why set up a full secrets manager when you can just put the key in a config file?
The answer is simple: because someone will leave the team, a server will be compromised, or a CI/CD pipeline will leak credentials — and by then the damage is done. This guide walks through practical, non-overengineered approaches to managing API keys and environment configuration that actually work for small teams.
The Core Principle: Separate Config from Code
The Twelve-Factor App methodology, specifically Factor 3 (Config), states that configuration should never be stored in the codebase. Config includes database credentials, API keys, OAuth tokens, and any value that varies between deployment environments — development, staging, production.
The reasoning is straightforward. When config lives in code:
- Every developer who clones the repo sees production credentials.
- Rotating a key requires a code change, a pull request, and a deploy.
- Accidental commits expose secrets in git history forever.
The alternative — storing config in environment variables — is not just a best practice. It is the minimum viable security posture for any application that touches external services.
What Environment Variables Actually Are
Environment variables are dynamic named values that affect how running processes behave. On Linux and macOS, you set them with export DB_PASSWORD=secret123. In a Node.js application, you access them through process.env.DB_PASSWORD. In Python, you use os.environ.get('DB_PASSWORD').
They are not encrypted. They are not a secrets manager. They are a mechanism for passing configuration into a running process without embedding it in code. Understanding this distinction matters because it prevents the common mistake of treating environment variables as a security solution when they are really a configuration delivery mechanism.
The .env File: Your First Line of Defense
For local development, the .env file is the standard approach. It is a plain text file, typically placed in the project root, that contains key-value pairs:
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_live_abc123
OPENAI_API_KEY=sk-proj-xyz789
The critical rule: never commit .env to version control. Add it to your .gitignore immediately:
# .gitignore
.env
.env.local
.env.*.local
This is not optional. A single committed .env file gives anyone with repository access every credential your application uses. The GitGuardian data confirms this is the most common leak vector.
For Node.js projects, the dotenv package loads these variables at startup:
require('dotenv').config();
const apiKey = process.env.OPENAI_API_KEY;
For Python projects, python-dotenv serves the same purpose:
from dotenv import load_dotenv
load_dotenv()
import os
api_key = os.environ.get('OPENAI_API_KEY')
The Production Gap: Where Small Teams Get Stuck
The .env file works for local development. It does not scale to production. Here is why:
In production, your application runs on a server or container. The environment variables must be injected by the deployment platform — not read from a file on disk. This is where the gap appears. Many small teams write .env files for local work and then manually SSH into their production server to set variables. This is brittle, error-prone, and creates an audit gap.
The Twelve-Factor methodology addresses this directly: each deployment has its own environment, managed independently. On platforms like Railway, Render, or Fly.io, you configure environment variables through the dashboard. On AWS, you use Systems Manager Parameter Store or Secrets Manager. On Docker, you pass variables at container launch time.
The principle is consistent: production secrets should never exist in your repository, your build artifacts, or your container image.
What OWASP Says About Secrets Management
The OWASP Secrets Management Cheat Sheet provides the most comprehensive public guidance on this topic. Its recommendations are designed for large organizations, but the underlying principles apply at any scale.
Three principles are especially relevant for small teams:
Centralize and standardize. Even a single secret manager reduces the attack surface compared to credentials scattered across config files, CI/CD variables, and developer machines. The OWASP sheet notes that standardization should include the full secrets lifecycle: creation, storage, access, rotation, revocation, and auditing.
Apply least privilege. Engineers should not have access to all secrets in a management system. For a small team, this means separating production credentials from development credentials and restricting who can view or rotate keys.
Automate rotation. The OWASP sheet emphasizes that secrets should not be static. API keys, database passwords, and certificates all have expiration windows. Manual rotation is a common failure point — a security engineer rotates a credential in AWS Secrets Manager but forgets to update the Terraform state file, blocking production deploys for hours.
The Practical Rotation Strategy for Indie Developers
You do not need a dedicated secrets management platform to rotate keys effectively. Here is what actually works for a small team:
-
Use short-lived keys where possible. Many API providers support temporary or scoped credentials. Use them.
-
Set calendar reminders for key expiration. If your provider does not support auto-rotation, manually track expiration dates. A 90-day rotation schedule is reasonable for most API keys.
-
Store rotation procedures in your runbook. Document exactly which files, environment variables, and infrastructure state must be updated when a key rotates. The post-mortem from the OWASP cheat sheet describes a scenario where a manual rotation failed because the runbook omitted the Terraform state update step. Do not repeat this mistake.
-
Audit access periodically. Even without a formal secrets manager, check who has access to your deployment platform’s environment variables and remove former team members promptly.
CI/CD: The Hidden Leak Point
Continuous integration and deployment pipelines are a frequent source of secret exposure. When a build system has access to production credentials, those credentials exist in build logs, cache artifacts, and temporary environment files.
To mitigate this:
- Store production secrets in your CI/CD platform’s encrypted secret store, not in repository files.
- Never echo or log environment variables during builds.
- Use separate CI/CD environments for development and production.
- Restrict which branches can access production secrets.
The OWASP cheat sheet dedicates a full section to CI/CD pipeline security. The core guidance is that secrets flowing through pipelines should be treated with the same rigor as secrets in production.
When to Consider a Dedicated Secrets Manager
For a single-server deployment with two developers, a dedicated secrets manager like HashiCorp Vault or AWS Secrets Manager is usually overkill. The operational overhead — authentication, access policies, integration testing — often exceeds the risk.
However, you should consider a secrets manager when:
- Your team grows beyond three or four developers.
- You deploy to multiple environments with different credentials.
- Compliance requirements demand audit trails for secret access.
- You manage certificates, TLS keys, or encryption keys in addition to API keys.
The decision is not binary. Many small teams start with .env files and local environment variables, then migrate to a secrets manager when the operational cost of manual management exceeds the cost of tooling.
Common Mistakes to Avoid
Hardcoding secrets in source code. This is the simplest mistake and the most dangerous. A single commit containing an API key gives attackers immediate access. Remove hardcoded secrets and rotate them immediately if they have been exposed.
Committing .env files to git. Once committed, a secret lives in git history forever. Even if you delete the file in a later commit, the secret remains recoverable. Use .gitignore and consider tools like git-secrets or pre-commit hooks to prevent accidental commits.
Treating environment variables as encrypted storage. Environment variables are transmitted in plaintext and stored in plaintext by your deployment platform. They protect against accidental exposure in code, not against intentional extraction.
Using the same credentials across environments. Development, staging, and production should each have separate credentials. A compromised development environment should never give an attacker access to production data.
A Realistic Checklist for Small Teams
- All API keys and credentials are stored in environment variables, not in source code.
-
.envfiles are listed in.gitignoreand never committed. - Production environment variables are configured through your deployment platform, not through files on a server.
- Each environment (development, staging, production) uses separate credentials.
- Key rotation dates are tracked and reminders are set.
- CI/CD pipelines use encrypted secret stores, not repository files.
- Former team members’ access is revoked promptly.
- Build logs do not contain or echo secret values.
FAQ
Do I need a secrets manager for a single-server app?
Not necessarily. Environment variables with proper .gitignore practices cover most small-team scenarios. A secrets manager becomes worthwhile when operational complexity grows.
Can I use the same .env file across all environments?
No. Each environment should have its own credentials. Use .env.local for developer-specific overrides and configure production variables through your deployment platform.
What if I accidentally committed a secret to git?
Rotate the secret immediately. Then use git filter-branch or BFG Repo-Cleaner to remove it from history, and add the file to .gitignore to prevent recurrence.
How often should I rotate API keys? At minimum every 90 days, or sooner if your provider supports shorter lifespans. Set calendar reminders and document the rotation procedure in your runbook.
Is using environment variables enough for security? Environment variables are a necessary foundation, not a complete solution. They prevent accidental exposure in code but do not provide encryption, access control, or auditing on their own. Combine them with platform-level secret management and CI/CD security practices for a complete posture.
Sources
- OWASP Secrets Management Cheat Sheet
- OWASP Secrets Management & Environment Variables Best Practices | AquilaX
- Twelve-Factor WordPress App #3: Config | Roots
- Can you keep a secret? - An Overview of the OWASP Secrets Management Cheat Sheet - Jet Anderson
- Master Secrets Management: An OWASP Guide
- Config - The Twelve Factor App Methodology - DEV Community
- Node.js app config using Twelve-Factor method
