# Configure Arena S3 result storage

Configure an ArenaJob to upload its output files to Amazon S3 or an S3-compatible store such as MinIO.

## Prerequisites

- Arena Fleet enabled in your Omnia installation.
- An existing S3 bucket, or an S3-compatible endpoint reachable from the cluster.
- Credentials with `s3:PutObject` on the bucket: a Secret holding an access key, or an IAM role on EKS.

## Configure the output

The worker stages its output files locally. When every work item has finished, it uploads them to `s3://<bucket>/<prefix>/<run-name>/<relative-path>`, so each run of a scheduled ArenaJob keeps its own files.

1. Choose how workers authenticate.

   **Static credentials.** Create a Secret in the ArenaJob's namespace with these two keys. The values below are placeholders:

   ```yaml
   apiVersion: v1
   kind: Secret
   metadata:
     name: arena-s3-credentials
     namespace: default
   type: Opaque
   stringData:
     AWS_ACCESS_KEY_ID: "<access-key-id>"
     AWS_SECRET_ACCESS_KEY: "<secret-access-key>"
   ```

   ```bash
   kubectl apply -f s3-credentials.yaml
   ```

   **IAM roles for service accounts (EKS).** Create an IAM policy that allows uploads, and a role that trusts a ServiceAccount:

   ```json
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Action": "s3:PutObject",
         "Resource": "arn:aws:s3:::my-arena-results/*"
       }
     ]
   }
   ```

   Annotate that ServiceAccount with the role ARN, then set the chart value `enterprise.arena.worker.serviceAccountName` to its name. Workers then run as that ServiceAccount. With the value empty, each run gets a generated `arena-worker` ServiceAccount that has no cloud identity.

   ```yaml
   enterprise:
     arena:
       worker:
         serviceAccountName: arena-worker-s3
   ```

   The ServiceAccount must exist in the namespace where the ArenaJob runs.

2. Add `output` to the ArenaJob. The worker requires `region`, even for S3-compatible storage. Omit `secretRef` when you use IAM roles.

   ```yaml
   apiVersion: omnia.altairalabs.ai/v1alpha1
   kind: ArenaJob
   metadata:
     name: nightly-eval
     namespace: default
   spec:
     sourceRef:
       name: my-eval-config
     type: evaluation
     workers:
       replicas: 2
     output:
       type: s3
       s3:
         bucket: my-arena-results
         prefix: "evaluations/nightly/"
         region: us-west-2
         secretRef:
           name: arena-s3-credentials
   ```

3. For MinIO or another S3-compatible store, add `endpoint`. The worker addresses buckets by path, so the bucket must already exist. Set `region` to any non-empty value, such as `us-east-1`.

   ```yaml
   output:
     type: s3
     s3:
       bucket: arena-results
       prefix: "evals/"
       region: us-east-1
       endpoint: http://minio.minio-system.svc:9000
       secretRef:
         name: minio-credentials
   ```

4. Apply the ArenaJob.

   ```bash
   kubectl apply -f arenajob.yaml
   ```

## Verify

1. Find the run and wait for it to finish.

   ```bash
   kubectl get arenaruns -l omnia.altairalabs.ai/arena-job=nightly-eval
   ```

2. Check the `OutputPersisted` condition. `True` names the destination.

   ```bash
   kubectl get arenarun <run-name> \
     -o jsonpath='{.status.conditions[?(@.type=="OutputPersisted")]}'
   ```

3. List the bucket.

   ```bash
   aws s3 ls s3://my-arena-results/evaluations/nightly/<run-name>/ --recursive
   ```

The upload contains the files the Arena engine writes to its output directory: media files, session recordings if the arena config enables them, and the data file of a `datagen` run. Per-scenario evaluation results are recorded in session-api, so a text-only evaluation can upload nothing.

## Troubleshooting

### The run succeeds but nothing is in the bucket

An upload failure does not fail the run. It sets `OutputPersisted=False` with reason `OutputUploadFailed` and emits a Warning event of the same reason; the message carries the first error. The worker logs it as `output upload failed`.

```bash
kubectl describe arenarun <run-name>
```

### S3 output config: region is required

Set `spec.output.s3.region`.

### Access denied

Confirm the credentials or role allow `s3:PutObject` on `<bucket>/<prefix>*`. If the Secret lacks `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY`, the worker ignores it and falls back to the default AWS credential chain.

### Endpoint connection errors

Confirm the endpoint is reachable from the worker pods, uses `http://` for a non-TLS service, and names an existing bucket.

## Related pages

- [ArenaJob reference](/reference/evaluation/arenajob/#output): every `output` field.
- [Helm values](/reference/platform/helm-values/#arena-fleet-configuration): Arena worker settings.
- [Monitor Arena jobs](/how-to/evaluation/monitor-arena-jobs/): track runs and find results.