How to Keep API Keys Out of Your Git Repository (With Code Examples)

Devops & Infrastructure, Security, and Tips & Tricks

How to Keep API Keys Out of Your Git Repository (With Code Examples)

It takes one git push with a hardcoded API key to compromise your entire infrastructure. Bots scan every public commit on GitHub within seconds, and even private repos are one misconfigured permission away from exposure. If you've ever committed a key by accident, you know the panic — rotating credentials, auditing access logs, hoping nothing was exploited in the window before you noticed.

This guide covers how to keep secrets out of your repository in the first place, with code examples and tooling that makes it automatic.

Why Hardcoded Keys Are Dangerous

When an API key ends up in a Git repository, it's not just in the latest commit — it's in the commit history forever (unless you rewrite it). Even if you delete the key in the next commit, anyone with access to the repository can find it with:

git log -p --all -S 'AKIA'  # Find any commit that added/removed an AWS key

Attackers don't even need repo access. Automated scanners trawl GitHub's public event stream and can extract pushed credentials within 30 seconds of the commit. AWS, Google Cloud, and GitHub all run their own secret scanning, but by the time they alert you, the key may already have been used.

The damage depends on what the key grants access to. A leaked Stripe key means financial exposure. A leaked AWS root key means someone can spin up crypto miners on your account. A leaked database connection string means your user data is compromised.

The Environment Variable Pattern

The solution is straightforward: never store secrets in code. Store them in environment variables that are loaded at runtime.

Node.js

// Install: npm install dotenv
require('dotenv').config();

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const dbUrl = process.env.DATABASE_URL;

Python

# Install: pip install python-dotenv
from dotenv import load_dotenv
import os

load_dotenv()

stripe_key = os.environ['STRIPE_SECRET_KEY']
db_url = os.environ['DATABASE_URL']

PHP

// Install: composer require vlucas/phpdotenv
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$stripeKey = $_ENV['STRIPE_SECRET_KEY'];
$dbUrl = $_ENV['DATABASE_URL'];

Ruby

# Install: gem install dotenv
require 'dotenv/load'

stripe_key = ENV.fetch('STRIPE_SECRET_KEY')
db_url = ENV.fetch('DATABASE_URL')

In each case, the actual values live in a .env file that sits on your machine (and on your server) but never enters version control.

Setting Up .env Files Properly

Create a .env file in your project root:

# .env - NEVER commit this file
STRIPE_SECRET_KEY=sk_live_abc123...
DATABASE_URL=postgres://user:pass@host:5432/mydb
SENDGRID_API_KEY=SG.xxxxx
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=wJalr...

Then make sure it's excluded from Git. Add these lines to your .gitignore:

# Environment files with secrets
.env
.env.local
.env.production
.env.*.local

# Editor-specific env files
.env.development.local
.env.test.local

Commit a .env.example file instead — same keys, no values:

# .env.example - commit this as a template for other developers
STRIPE_SECRET_KEY=
DATABASE_URL=
SENDGRID_API_KEY=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=

This way, new team members know which environment variables they need to set up, without ever seeing the actual secrets.

Getting Secrets to Your Server

Your local .env file works for development. But how do secrets get to production?

Option 1: DeployHQ Config Files

DeployHQ's Config Files feature lets you store files that get placed on your server during deployment but are never stored in your Git repository. This is ideal for .env files:

  1. Go to your project in DeployHQ
  2. Navigate to Config Files
  3. Add a new config file with the path .env (or wherever your app reads it)
  4. Paste your production environment variables
  5. DeployHQ will copy this file to your server on every deploy

The config file contents are stored encrypted in DeployHQ — they never touch your repository.

Option 2: Encrypted Environment Variables with dotenvx

For teams that want secrets in the repository (encrypted), dotenvx provides a solution. We wrote a full walkthrough on deploying PHP applications with encrypted environment variables using dotenvx and DeployHQ — the approach works for any language.

Option 3: Server-Side Environment Variables

Set variables directly on your server (via systemd, Docker, or your hosting provider's dashboard) and skip .env files on production entirely. This is the most secure approach, but requires server access for every change.

flowchart TD
    A[Developer] -->|.env file| B[Local Machine]
    A -->|.env.example| C[Git Repository]
    C -->|No secrets| D[DeployHQ]
    D -->|Code| E[Production Server]
    F[DeployHQ Config Files] -->|Encrypted .env| E
    G[Server Environment] -->|System vars| E

Automated Secret Scanning

Don't rely on humans to never make mistakes. Set up automated scanning that catches leaked secrets before they reach your remote repository.

git-secrets (AWS)

# Install
brew install git-secrets  # macOS
# or
git clone https://github.com/awslabs/git-secrets.git && cd git-secrets && make install

# Set up in your repo
cd your-project
git secrets --install
git secrets --register-aws  # Adds AWS key patterns

# Now any commit with AWS keys will be blocked:
git commit -m "add config"
# ERROR: Matched one or more prohibited patterns

truffleHog

Scans your entire Git history for high-entropy strings and known secret patterns:

# Install
pip install trufflehog

# Scan your repo
trufflehog git file://. --only-verified

GitHub Secret Scanning

If you use GitHub, enable secret scanning alerts in your repository settings. GitHub partners with service providers (AWS, Stripe, Twilio, etc.) to automatically detect and alert on leaked credentials. For GitHub Advanced Security users, you can also enable push protection — which blocks the push before the secret ever reaches GitHub.

Pre-commit Hooks

Add a pre-commit hook that checks for common secret patterns:

#!/bin/sh
# .git/hooks/pre-commit

# Check for common secret patterns
if git diff --cached --diff-filter=ACM | grep -qE '(AKIA[0-9A-Z]{16}|sk_live_|SG\.[a-zA-Z0-9]{22}\.|ghp_[a-zA-Z0-9]{36})'; then
    echo "ERROR: Potential secret detected in staged changes"
    echo "Review your changes and remove any API keys before committing"
    exit 1
fi

What to Do If You've Already Leaked a Key

If a secret made it into your Git history, deleting it in a new commit is not enough. The old commit still contains the key. Here's the response playbook:

  1. Rotate the key immediately. Go to the service provider's dashboard and generate a new key. Revoke the old one. This is step one, before anything else.

  2. Update the new key in your production environment (DeployHQ config files, server env vars, etc.)

  3. Check access logs. Most API providers show recent usage. Look for unauthorized requests between when the key was committed and when you rotated it.

  4. Remove from Git history if the repo is public. Use git filter-branch or BFG Repo-Cleaner:

# Using BFG (faster and simpler than filter-branch)
bfg --replace-text passwords.txt  # passwords.txt contains the leaked secrets
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --force
  1. Set up scanning (see previous section) so it doesn't happen again.

Deployment Security Checklist

When deploying applications through DeployHQ, use this checklist to ensure your secrets are protected at every stage:

  • [ ] .env files are in .gitignore
  • [ ] .env.example is committed with empty values
  • [ ] Production secrets are in DeployHQ Config Files or server environment variables
  • [ ] git-secrets or equivalent pre-commit hook is installed
  • [ ] GitHub secret scanning is enabled (if using GitHub)
  • [ ] API keys have minimal permissions (principle of least privilege)
  • [ ] Key rotation is scheduled (quarterly at minimum)
  • [ ] Deployment access is controlled — only authorized team members can trigger deploys
  • [ ] Server access uses SSH keys, not passwords (deploying to private networks)

For a broader deployment security overview, see our guide on practical security tips for smarter deployments.


Ready to deploy with your secrets properly managed? Sign up for DeployHQ — Config Files for secure secret management are included on all plans.

For questions about deployment security, reach out at support@deployhq.com or on Twitter/X.