Skip to main content
By default the Enterprise stack runs Postgres and MinIO inside the deploy boundary. For any production AWS deployment we recommend backing these onto RDS Postgres, real S3, and DynamoDB instead — better durability, point-in-time recovery, IAM-driven credentials, and operational ownership by AWS. This page covers the AWS-side setup and the config knobs. The same managed-resource path works on both the Compose and EKS deploys.

RDS Postgres

Instance setup

  • Engine: PostgreSQL 16 (Aurora PostgreSQL 16 also works)
  • Database: create the nebula logical database before application bootstrap with nebula-enterprise postgres provision, unless your platform team already manages database/user creation through its own workflow. The application role does not need CREATEDB.
  • Parameter group: vector, pg_partman, and pg_cron must be available to the master user; shared_preload_libraries must include pg_cron; cron.database_name must 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 :5432 from 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 at infra/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:
The scripts keep Terraform state in 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:
Keep the admin password out of 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:
Set 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:
For Compose installs, run the same helper with --skip-k8s-secrets and an explicit NEBULA_POSTGRES_PASSWORD value in the environment, then place that same password in env/.env.enterprise.
Then assert the database-side contract:
For the EKS installer, set these values in nebula-install.env and run the normal install:
If you used 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 your your-values.yaml (copied from helm/examples/eks/values.yaml):
The provisioner creates this Secret for Kubernetes installs. If your platform workflow creates it instead, preserve the same shape:
  • postgres.credentialsSecret (Nebula application DB): Kubernetes Secret with username and password keys (those exact lowercase key names — the chart reads them via secretKeyRef.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-uploads prefix 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:
If you’re using SSE-KMS, also grant kms:Encrypt, kms:Decrypt, kms:GenerateDataKey on the KMS key ARN.

Compose: wire up via .env.enterprise

Append to env/.env.enterprise:
The = 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 at helm/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 creates pk / sk string-key tables with on-demand billing and bootstraps the writer-authority records required by the runtime:
For DynamoDB-compatible endpoints, add --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:
Leave 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:
  1. Dump in-stack Postgresdocker exec -t <postgres-container> pg_dumpall -U postgres > nebula-dump.sql
  2. Restore into RDSpsql -h <rds-endpoint> -U postgres < nebula-dump.sql
  3. Copy MinIO contents to S3aws s3 sync s3://nebula-files/ s3://<your-bucket>/ --source-region us-east-1 --region us-east-1 (with appropriate MinIO/S3 credentials)
  4. Update .env.enterprise with the managed-resource overrides above
  5. Restart: ./enterprise/bootstrap.sh — the script will pick up the overrides and skip the in-stack services
Test on a staging deployment before doing this in production; the cutover involves a brief downtime window between the in-stack stop and the managed-resource start.