1. Create the bucket
Create a private bucket in the same region as your cluster, with:- All public access blocked
- Server-side encryption (AES256 or KMS)
- A bucket policy that denies any request where
aws:SecureTransport=false
<your-org>-opal-exports). Don’t share it with other applications or other environments.
Versioning and CORS are not required. Downloads are served through the Opal backend, not directly from the browser.
2. Create an IAM user and access key
Opal authenticates to the bucket with a static access key pair. This is a two-step process: create a dedicated IAM user with a scoped policy, then issue an access key for that user.2a. Create the IAM user
Create a dedicated IAM user (e.g.opal-exports-service) and attach a policy with only these permissions:
2b. Issue an access key
Generate one access key pair for the user (aws iam create-access-key --user-name opal-exports-service, or via the IAM console). Store both the access key ID and secret access key in your secret manager — you’ll paste them in step 3.
If you provision the user via infrastructure-as-code, create the access key separately so the secret stays out of state. The secret access key is shown only once at creation time.
IAM Roles for Service Accounts (IRSA) and Workload Identity are not supported today. Static access keys are the only authentication method.
3. Update Opal Configuration
Pick the section that matches your install method.KOTS
Open the admin console and find the Async Exports Storage section:- Click Enable Async Exports.
- Storage type: leave as AWS S3 (default).
- Bucket name:
my-org-opal-exports - Region: e.g.
us-east-2 - Access key ID and secret access key from step 2.
Helm
Add the following to your helm values:helm upgrade to apply the configuration.
4. Verify
Run a query in Opal Query and trigger an export. You should get a download link and be able to download the exported data. Async exports are currently only supported via Opal Query.Alternative: Use Google Cloud Storage (GCS)
If you run Opal on GCP and would rather not provision AWS infrastructure, GCS works via its interoperability mode — an S3-compatible XML API. The setup mirrors the AWS flow above with three differences:- You create a GCS bucket instead of an S3 bucket.
- You generate an HMAC key for a service account instead of an IAM user access key.
- You point Opal at GCS’ S3-compatible endpoint (
https://storage.googleapis.com) instead of an AWS region.
1. Create the GCS bucket
In your GCP project, create a bucket with:- Location type: Region (close to your cluster)
- Access control: Uniform (recommended)
- Public access prevention: Enforced
- Encryption: Google-managed (default)
2. Create a service account and HMAC key
- IAM & Admin → Service accounts → create a dedicated service account (e.g.
opal-exports-service). Skip the “grant access to project” step — we’ll scope to the bucket instead. - Cloud Storage → your bucket → Permissions → Grant access → assign the service account the Storage Object Admin role on this bucket.
- Cloud Storage → Settings → Interoperability → Create access key for service account → select your service account.
- Save the Access key (starts with
GOOG1...) and Secret. The secret is shown only once.
3. Update Opal Configuration
KOTS:- Click Enable Async Exports.
- Storage type: select S3-compatible (e.g. GCS).
- Bucket name: your GCS bucket name.
- Endpoint URL:
https://storage.googleapis.com - Access key ID and Secret access key: from step 2.
endpoint instead of region:
helm upgrade to apply the configuration.
4. Verify
Same as the AWS flow: run a query in Opal Query, trigger an export, and confirm a.zip appears in your GCS bucket under exports/<org-id>/<job-id>.zip.
