Environment Variables
All environment variables used to configure Surface Security backend services.
Core Infrastructure
| Variable | Description | Default |
|---|---|---|
SERVER_ADDR | API server bind address | :8090 (Helm), :8080 (K8s manifests/Pulumi) |
DEV_MODE | Enable development mode (skips license check) | Auto-detected from bind address |
The Go default for SERVER_ADDR is :8090, but Kubernetes base manifests and Pulumi override this to :8080. The Helm chart uses :8090. Ensure your ingress/service configuration targets the correct port for your deployment method.
Database: PostgreSQL
| Variable | Description | Default |
|---|---|---|
PG_HOST | PostgreSQL host | localhost |
PG_PORT | PostgreSQL port | 5432 |
PG_DATABASE | PostgreSQL database name | surfacesec |
PG_USER | PostgreSQL user | surfacesec |
PG_PASSWORD | PostgreSQL password | (required) |
PG_MAX_CONNS | Max connections per pod | 50 (API), 30 (Ingestion) |
PG_MIN_CONNS | Min idle connections per pod | 10 (API), 5 (Ingestion) |
Database: ClickHouse
| Variable | Description | Default |
|---|---|---|
CH_HOSTS | ClickHouse hosts (comma-separated) | localhost:9000 |
CH_DATABASE | ClickHouse database name | surfacesec |
CH_USER | ClickHouse user | surfacesec |
CH_PASSWORD | ClickHouse password | (required) |
CH_MAX_OPEN_CONNS | Max open connections per pod | 30 (API), 40 (Ingestion) |
CH_MAX_IDLE_CONNS | Max idle connections per pod | 15 (API), 20 (Ingestion) |
Streaming: Redpanda
| Variable | Description | Default |
|---|---|---|
KAFKA_BROKERS | Redpanda broker endpoints (comma-separated) | localhost:19092 |
Object Storage: MinIO
| Variable | Description | Default |
|---|---|---|
MINIO_ENDPOINT | MinIO endpoint | localhost:9100 |
MINIO_ACCESS_KEY | MinIO access key | surfacesec |
MINIO_SECRET_KEY | MinIO secret key | (required) |
MINIO_BUCKET_SCREENSHOTS | Bucket name for phishing screenshots | surfacesec-screenshots |
In Docker Compose, MinIO is exposed on ports 9500 (API) and 9501 (Console). In Kubernetes, the standard ports 9000 (API) and 9001 (Console) are used.
Security
| Variable | Description | Default |
|---|---|---|
CERT_ENCRYPTION_KEY | Hex-encoded 32-byte key for mTLS CA private key encryption | Auto-generated in dev mode |
SPIFFE_DOMAIN | SPIFFE trust domain for device certificate URI SANs | surfacesec.local |
EXTENSION_SIGNING_KEY | Base64-encoded Ed25519 seed for signing extension manifests | (empty) |
ALLOWED_ORIGINS | CORS allowed origins | (none) |
The raw hex string is SHA256-hashed internally to derive the 32-byte ChaCha20-Poly1305 encryption key. Losing the original hex value means losing the ability to decrypt stored CA private keys. Generate with openssl rand -hex 32 and back up securely.
This value is embedded in device certificate URI SANs as spiffe://{domain}/device/{deviceID}. It must match the match_typed_subject_alt_names prefix configured in Envoy. If you change one, you must change both. Production deployments should set this to their own domain (e.g., surfacesec.yourcompany.com).
Licensing and Portal
| Variable | Required | Description | Default |
|---|---|---|---|
SURFACE_LICENSE_KEY | Yes (production) | Ed25519-signed license key (base64 JSON) | (none) |
SURFACE_PUBLIC_KEY | Yes (production) | Ed25519 public key for license verification (PEM or base64) | (none) |
SURFACE_PUBLIC_KEY_FILE | Alternative | Path to file containing the public key | (none) |
SURFACE_PORTAL_URL | No | Portal URL for heartbeats and sync; empty = offline mode | https://portal.surface-security.com |
SURFACE_PORTAL_API_TOKEN | If portal URL set | Bearer token for portal API authentication | (none) |
PORTAL_HEARTBEAT_INTERVAL_SECONDS | No | Seconds between heartbeats to the portal | 21600 (6 hours; all deployed K8s/Helm configs override to 300s/5min) |
Scaling
These variables control connection pool sizes and worker counts. Proper sizing is critical for multi-pod deployments.
| Variable | Service | Default | Description |
|---|---|---|---|
PG_MAX_CONNS | API | 50 | PostgreSQL pool size per pod |
PG_MIN_CONNS | API | 10 | Minimum idle PG connections |
PG_MAX_CONNS | Ingestion | 30 | PostgreSQL pool size per pod |
PG_MIN_CONNS | Ingestion | 5 | Minimum idle PG connections |
CH_MAX_OPEN_CONNS | API | 30 | ClickHouse pool size per pod |
CH_MAX_IDLE_CONNS | API | 15 | ClickHouse idle connections |
CH_MAX_OPEN_CONNS | Ingestion | 40 | ClickHouse pool size per pod |
CH_MAX_IDLE_CONNS | Ingestion | 20 | ClickHouse idle connections |
NUM_WORKERS | Ingestion | 8 | Concurrent event processors |
HEALTH_ADDR | Ingestion | :9090 | Health HTTP server bind address |
RATE_LIMIT_RPS | API | 100 | Per-client rate limit (requests/second) |
Scaling Reference
Use this table to size connection pools based on extension fleet size:
| Extensions | API Pods | Ingestion Pods | Total PG Conns | Total CH Conns |
|---|---|---|---|---|
| 1,000 | 3 | 3 | 150 | 90 |
| 5,000 | 5 | 5 | 250 | 150 |
| 10,000 | 10 | 8 | 500 | 240 |
| 25,000 | 15 | 12 | 750 | 360 |
| 50,000 | 20 | 15 | 1,000 | 450 |
Formula: total_db_connections = max_pods x pool_size_per_pod
Versioning
| Variable | Description | Default |
|---|---|---|
SURFACESEC_VERSION | Application version reported by health endpoint and update controller | 1.0.0 |
IMAGE_REGISTRY_URL | Local registry for embedded update images | (empty) |
REGISTRY_USERNAME | Registry basic auth username | (empty) |
REGISTRY_PASSWORD | Registry basic auth password | (empty) |
Version resolution priority (checked in order at startup):
SURFACESEC_VERSIONenvironment variableBuildVersioncompiled via-ldflags -X(fromBUILD_VERSIONbuild arg)- Kubernetes deployment annotation
/etc/surfacesec/versionfile- Fallback
"1.0.0"
Search
| Variable | Description | Default |
|---|---|---|
SEARCH_BACKEND | Search backend: postgres or opensearch | postgres (Go binary default); docker-compose overrides to opensearch for dev |
OPENSEARCH_URL | OpenSearch endpoint URL | http://opensearch:9200 |