Skip to main content
Lambda is trickier than long-running hosts: you can’t wrap the handler with capy run at invoke time, because Lambda invokes your function directly (not a shell). Two viable patterns, depending on how you package the function.

Pattern 1 - Container image with capy run as the Lambda entrypoint

Lambda container images let you override the image’s ENTRYPOINT, so capy run can wrap the Lambda runtime process itself. That puts decryption at init time, not per-invocation: capy run decrypts once when the container cold-starts, then spawns the Lambda runtime with the plaintext values in its environment. That runtime process serves every invocation - Capy is not in the request path.
Mint the pair with capy deploy. The platform picker has no Lambda entry - pick Other…, or AWS CDK if that’s how you ship - and Capy opens a page showing the SECRETS_BLOB and PROJECT_KEY values. Set both on the Lambda’s configuration (AWS console, CDK, SAM, or Terraform). One service fetch per cold start; warm invocations reuse the in-memory plaintext. That fetch is an HTTPS call to https://api.capy.sc (override with CAPY_API_URL), so a VPC-attached function needs outbound egress - if the call fails, capy run exits without starting the runtime. Anything already present in the Lambda’s own environment wins over a decrypted value of the same name, so don’t set a plaintext DATABASE_URL alongside the two Capy vars and expect the encrypted one to take effect.

Pattern 2 - Zip deploy with build-time inline

For zip-package Lambdas (SAM, Serverless Framework, CDK using NodejsFunction), the build step can bundle plaintext values into the function code:
During the build, capy run decrypts the capy:-prefixed values in .env and passes them in the environment of the command it spawns. Your IaC tool reads process.env and sets the Lambda’s Environment.Variables config - Lambda stores those plaintext on AWS’s side. Example with SAM:
Deploy with capy run wrapping the deploy command so the parameters are populated from decrypted env:
The bash -c and the single quotes matter. capy run puts the decrypted values in the environment of the process it spawns, never in your shell, so $DATABASE_URL has to be expanded inside that process - write it unquoted and your shell substitutes an empty string before capy run ever starts. AWS stores the parameter values plaintext on the Lambda config. Matches the Vercel / Cloudflare build-time inline tradeoff: your cloud provider sees plaintext, but the Capy service never does, and the app has zero runtime crypto overhead.

Which pattern to pick

  • Container image + capy run entrypoint: preserves the “secrets never touch AWS config plaintext” property. Costs one service round trip per cold start, and the function needs egress to reach the Capy API.
  • Zip deploy + build-time inline: AWS has plaintext on the Lambda config, no runtime overhead. Simpler. Same trust model as pasting into the Lambda console directly - except the plaintext only lives in AWS, not in git / local env files.
Most teams already trust AWS with Lambda config env vars, so pattern 2 is usually the right call. Reach for pattern 1 when you’ve explicitly decided AWS shouldn’t see plaintext.

Revocation

  • Pattern 1: capy deploy list shows this project’s deploy tokens - id prefix, status, created date - and capy deploy revoke <deployId> takes that prefix. The service then refuses the cold-start fetch, so new cold starts fail while warm Lambdas keep serving until idle-killed.
  • Pattern 2: AWS has plaintext env vars - revoking the Capy deploy token does nothing for already-deployed functions. Change the values themselves: rotate the upstream credential, save the new value with capy edit, then re-run the deploy so AWS gets the new values.
Last modified on August 11, 2026