RDS Postgres
Instance setup
- Engine: PostgreSQL 16 (Aurora PostgreSQL 16 also works)
- Database: create the
nebulalogical database before application bootstrap withnebula-enterprise postgres provision, unless your platform team already manages database/user creation through its own workflow. The application role does not needCREATEDB. - Parameter group:
vector,pg_partman, andpg_cronmust be available to the master user;shared_preload_librariesmust includepg_cron;cron.database_namemust match the Nebula database name. Changing these settings may require a cluster reboot before bootstrap. - Network: private subnet in the same VPC as the compose host or EKS cluster. The host’s / cluster’s security group must be allowed inbound on
:5432from the application security group. - Storage: gp3, 100 GB minimum to start. Enable autoscaling up to ~1 TB; Nebula’s working set scales linearly with the number of collections + total ingested document volume.
- Backups: RDS automatic backups, 7-day retention minimum. PITR is enabled by default.
AWS Terraform / OpenTofu
The Enterprise bundle includes a guided AWS workspace generator plus a focused RDS module atinfra/aws-rds-postgres. The module creates the physical RDS PostgreSQL 16 instance, subnet group, security group, parameter group, RDS-managed master password, backups, CloudWatch Postgres log export, encryption, and Multi-AZ storage shape. It does not create the Nebula role or database; run the logical bootstrap below after the instance is available.
For first-time EKS installs, generate a workspace and run the emitted phase scripts:
nebula-prod-install/terraform/aws-rds-postgres, write nebula-install.env with mode 0600, seed the AWS Secrets Manager app secret, fetch the RDS-managed admin password into PGPASSWORD, bootstrap and verify Postgres, run Helm, and verify the rollout. Export provider keys such as OPENAI_API_KEY before 00-seed-app-secret.sh; to update an existing secret, edit nebula-prod-install/app-secret.json and rerun with UPDATE_APP_SECRET=1.
If you want to run Terraform manually instead, use the bundled module directly:
nebula-install.env; prefer PGPASSWORD or .pgpass. If you do put any password value in the env file, run chmod 600 nebula-install.env before writing it.
Database / user provisioning
Use the bundle helper as the canonical external Postgres bootstrap. It creates the Nebula role, database, required extensions, and Kubernetes credential Secret with the shape the Helm chart expects:PGPASSWORD for the admin role. If you do not pass NEBULA_POSTGRES_PASSWORD in the environment, the helper generates a stable random password, reusing the existing Kubernetes Secret on rerun.
Pass --nebula-host only when application pods should connect through a hostname different from the --admin-url host.
To assert an existing setup without changing Postgres or Kubernetes, run the verifier with the same arguments. It checks the database objects, required extensions, Secret shapes, and that the Secret credentials can authenticate with SELECT 1:
--skip-k8s-secrets and an explicit NEBULA_POSTGRES_PASSWORD value in the environment, then place that same password in env/.env.enterprise.
nebula-install.env and run the normal install:
infra/aws-rds-postgres, terraform output -raw nebula_install_env writes the Postgres values for this block. Fetch the admin password from the RDS-managed Secrets Manager secret as shown above.
Before running the installer, set PGPASSWORD for the admin role or use .pgpass. If you set any password value in nebula-install.env, protect that file with chmod 600 before writing it.
If your platform team provisions Postgres outside the bundle helper, mirror the same contract: a Nebula user/database, vector / pg_partman / pg_cron installed in the Nebula database, and a Kubernetes Secret with username and password keys. Run nebula-enterprise postgres verify before Helm install to check that contract without granting the helper permission to mutate resources.
Compose: wire up via .env.enterprise
Append to env/.env.enterprise:
bootstrap.sh auto-detects the override and skips the in-stack postgres container via compose profiles. No other changes required.
EKS: wire up via Helm values
In youryour-values.yaml (copied from helm/examples/eks/values.yaml):
postgres.credentialsSecret(Nebula application DB): Kubernetes Secret withusernameandpasswordkeys (those exact lowercase key names — the chart reads them viasecretKeyRef.key: username/.key: password).
S3 (object storage)
Bucket setup
- Region: same as the compose host / EKS cluster (cross-region adds latency to every snapshot read)
- Versioning: enabled (recommended; protects against accidental deletes)
- Encryption: SSE-S3 or SSE-KMS
- Public access: blocked at the account level
- Lifecycle: optional — Nebula doesn’t expire its own objects, but you can set a policy on the
incomplete-multipart-uploadsprefix to clean up failed ingests after 7 days
IAM policy
The principal accessing S3 (instance profile / ECS task role / EKS IRSA role / IAM user) needs:kms:Encrypt, kms:Decrypt, kms:GenerateDataKey on the KMS key ARN.
Compose: wire up via .env.enterprise
Append to env/.env.enterprise:
= with no value (e.g. NEBULA_S3_ENDPOINT_URL=) is intentional — it tells the AWS SDK to resolve the regional endpoint instead of falling back to the in-stack MinIO URL, and tells boto3 to use the default credential chain (instance profile, ECS task role) instead of static keys.
bootstrap.sh auto-detects NEBULA_USE_EXTERNAL_S3=1 and skips the in-stack minio + minio-init containers.
EKS: wire up via Helm values
The example values file athelm/examples/eks/values.yaml has this pre-wired:
DynamoDB orchestration state
Nebula stores durable orchestration state in four DynamoDB tables. Create or verify those tables before Helm install. The helper createspk / sk string-key tables with on-demand billing and bootstraps the writer-authority records required by the runtime:
--endpoint-url <endpoint> and use the same endpoint in Helm values.
Grant the Nebula runtime principal access to the four tables for GetItem, PutItem, DeleteItem, Query, BatchGetItem, BatchWriteItem, UpdateItem, ConditionCheckItem, and DescribeTable. DynamoDB transaction writes are authorized through those underlying item permissions plus ConditionCheckItem; the endpoint must still support the TransactWriteItems API. Then wire the table names into Helm values:
NEBULA_ORCHESTRATION_DYNAMODB_ENDPOINT_URL empty for AWS DynamoDB so boto3 uses the regional endpoint.
Sanity checks
After re-bootstrapping with managed resources, verify:Migrating an existing in-stack deploy to managed resources
If you’ve been running with in-stack Postgres + MinIO and want to move to RDS + S3 without losing data:- Dump in-stack Postgres —
docker exec -t <postgres-container> pg_dumpall -U postgres > nebula-dump.sql - Restore into RDS —
psql -h <rds-endpoint> -U postgres < nebula-dump.sql - Copy MinIO contents to S3 —
aws s3 sync s3://nebula-files/ s3://<your-bucket>/ --source-region us-east-1 --region us-east-1(with appropriate MinIO/S3 credentials) - Update
.env.enterprisewith the managed-resource overrides above - Restart:
./enterprise/bootstrap.sh— the script will pick up the overrides and skip the in-stack services