n4nAI

Storing provider keys in Google Secret Manager for Cloud Functions

Learn how to store and access google secret manager cloud functions api keys securely, with step-by-step setup and Python code for Cloud Functions.

n4n Team3 min read692 words

Audio narration

Coming soon — every post will get a voice note here.

Hardcoding credentials in your deployment package is how breaches start. This guide walks through storing google secret manager cloud functions api keys outside your source tree and pulling them at runtime with minimal latency and correct IAM scoping.

Why environment variables aren’t enough

Baking API keys into a Docker image or a .env file committed to Git is a liability. Even if you restrict repo access, the key lives in the deployment artifact and in instance memory where anyone with read access to the function’s storage can grab it. Google Secret Manager gives you centralized access control, versioning, and audit logging. For serverless workloads, the right pattern is to grant the function’s service account read access to a specific secret and fetch it at cold start.

Step 1: Create the secret in Google Secret Manager

Create one secret per provider key. Avoid stuffing multiple keys into a single JSON blob unless you want rotation to invalidate everything at once.

gcloud secrets create openai_api_key --replication-policy=automatic
echo -n "sk-your-real-key-here" | gcloud secrets versions add openai_api_key --data-file=-

The --replication-policy=automatic flag replicates the secret across regions, which matters if you deploy functions in multiple regions. When you store google secret manager cloud functions api keys this way, each versions add call creates an immutable version. You can list them:

gcloud secrets versions list openai_api_key

Keep the secret ID lowercase and free of underscores if you plan to reference it from Terraform later; hyphens are safer.

Step 2: Grant the Cloud Functions service account access

Cloud Functions (2nd gen) runs under a service account, usually PROJECT_ID@appspot.gserviceaccount.com for Gen1 or a dedicated Compute Engine SA for Gen2. Grant only roles/secretmanager.secretAccessor on the specific secret, not the whole project.

gcloud secrets add-iam-policy-binding openai_api_key \
  --member="serviceAccount:my-func-sa@my-project.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"

The pattern for google secret manager cloud functions api keys requires least privilege: the function can read this secret and nothing else. If you reuse the default App Engine SA, you’ve likely handed it broad project rights already—create a dedicated SA and bind only what the function needs.

Step 3: Write the accessor code in your function

Install the client library in your requirements.txt:

google-cloud-secret-manager==2.20.0
functions-framework==3.6.0

Cache the secret in a module-level variable so you only pay the round-trip on cold starts. Do not log the value.

import os
from google.cloud import secretmanager

_PROJECT = os.environ.get("GCP_PROJECT")
_SECRET_ID = "openai_api_key"
_cache = {}

def get_api_key(version="latest"):
    if version in _cache:
        return _cache[version]
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{_PROJECT}/secrets/{_SECRET_ID}/versions/{version}"
    resp = client.access_secret_version(request={"name": name})
    key = resp.payload.data.decode("utf-8")
    _cache[version] = key
    return key

This code assumes GCP_PROJECT is set in the runtime (it is by default on Cloud Functions). If you run locally, export it or hardcode for tests.

Step 4: Call your LLM provider using the fetched key

Wire the key into your HTTP handler. Below is a minimal OpenAI call, but the same shape works for any OpenAI-compatible endpoint.

import functions_framework
from openai import OpenAI

@functions_framework.http
def chat(request):
    key = get_api_key()
    client = OpenAI(api_key=key)
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "ping"}]
    )
    return resp.choices[0].message.content, 200

If you’d rather front multiple providers with one OpenAI-compatible endpoint, n4n.ai exposes a single key that still needs secure storage; the Secret Manager pattern above is identical, just point the client at https://api.n4n.ai/v1 and use that key. The gateway handles fallback across 240+ models behind one credential.

Step 5: Deploy the Cloud Function

Deploy with an explicit service account and runtime. Do not pass the key as an environment variable.

gcloud functions deploy chat \
  --runtime=python311 \
  --trigger-http \
  --entry-point=chat \
  --service-account=my-func-sa@my-project.iam.gserviceaccount.com \
  --region=us-central1 \
  --allow-unauthenticated

For Gen2, the command is the same but behind the scenes it creates a Cloud Run service. Deploying with google secret manager cloud functions api keys means your deployment artifact contains zero secrets; the IAM binding is the only thing that grants access.

Step 6: Verify the integration works

Hit the function and check the response plus logs:

curl https://us-central1-my-project.cloudfunctions.net/chat
gcloud functions logs read chat --region=us-central1 --limit=50

A successful call returns model output and shows no PermissionDenied errors in the logs. If you see 403, the SA binding is wrong. If you see NotFound, the secret ID or project env var is off.

To prove the key is not in the image, run:

gcloud functions describe chat --region=us-central1 | grep -i "env"

No openai_api_key entry should appear.

Operational notes: rotation and caching

Secret Manager versions are immutable. To rotate, add a new version and update your code’s version parameter or switch to a labeled alias. Because the function caches at cold start, a rotation requires a new deployment or an instance restart to pick up the latest version—unless you implement a TTL check in get_api_key.

For high-throughput functions, fetching the secret every invocation adds ~10–30 ms. Cache it. For stricter security, use a short TTL (e.g., 5 minutes) and re-fetch. Never write the key to a temp file or return it in responses.

The IAM binding is the real security boundary. Review it quarterly with gcloud secrets get-iam-policy openai_api_key. If a key leaks, disable the version immediately—gcloud secrets versions disable 1 openai_api_key—and rotate.

Tagsgoogle-cloud-functionssecret-managersecurityapi-keys

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All google cloud functions & cloud run llm integration posts →