Environment and deployment
Environment variables, separating environments, CI, serverless, worker pools, and self-hosting the backend.
Environment variables
The SDK reads no configuration file. These are conventions your code passes to
init().
| Variable | Purpose |
|---|---|
CAPSERA_API_KEY | Your cap- key. |
CAPSERA_ENDPOINT | Backend URL, for self-hosting. |
capsera.init(
api_key=os.environ["CAPSERA_API_KEY"],
endpoint=os.environ.get("CAPSERA_ENDPOINT", "https://api.capsera.ai"),
)
The guard pattern, which Capsera's auto-instrumentation writes into repositories:
if os.environ.get("CAPSERA_API_KEY"):
capsera.init(api_key=os.environ["CAPSERA_API_KEY"])
Without a key the SDK stays inactive, so the same code runs unchanged on a machine with no Capsera credentials. The cost is that a missing key is silent, which is why Verify it works exists.
Separating environments
Use a separate API key per environment and pass a matching env label:
capsera.init(api_key=os.environ["CAPSERA_API_KEY"], env="production")
The key determines which workspace receives events. env labels them within it.
Sharing one key across development and production puts load-test traffic in the same
reports as customer spend.
Long-running services
Call init() at startup. The background worker ships every 500 ms and the daemon
thread does not delay shutdown.
capsera.init(api_key=os.environ["CAPSERA_API_KEY"], env=settings.env)
app = FastAPI()
Add set_session() in middleware for per-request grouping. See
Tags and sessions.
Short-lived processes
Scripts, cron jobs, CI steps, and one-shot workers must drain before exit, because the daemon thread ends with the process:
try:
main()
finally:
capsera.shutdown()
This is the second most common cause of an empty dashboard, after a missing key.
Serverless
Initialise outside the handler so it runs once per container rather than once per invocation:
import os
import capsera
capsera.init(api_key=os.environ["CAPSERA_API_KEY"], env="production")
def handler(event, context):
result = do_work(event)
capsera.flush()
return result
Flush before returning. A serverless runtime can freeze the execution environment
immediately after the handler returns, and a frozen thread ships nothing. Use flush()
rather than shutdown(), so a warm container still has a worker for the next
invocation.
Celery and worker pools
Initialise once per worker process, not per task. With prefork, use the
worker-process init hook:
from celery.signals import worker_process_init
@worker_process_init.connect
def start_capsera(**_):
capsera.init(api_key=os.environ["CAPSERA_API_KEY"], env="production")
Initialising in the parent before forking gives each child a copy of a thread that does not exist in it. The queue fills and nothing ships.
CI
Set the key only if you want test-suite spend recorded. Often you do, because a test suite making real provider calls is a real cost that tends to grow unnoticed.
env:
CAPSERA_API_KEY: ${{ secrets.CAPSERA_API_KEY }}
Label it and drain at the end:
capsera.init(api_key=os.environ["CAPSERA_API_KEY"], env="ci", agent_name="test-suite")
To report nothing from CI, leave the variable unset. With the guard pattern the SDK is then inactive with no code change.
Self-hosting
Point endpoint at your own deployment:
capsera.init(
api_key=os.environ["CAPSERA_API_KEY"],
endpoint="https://capsera.internal.example.com",
)
Everything described in Privacy then stays inside your infrastructure.