# Amazon S3 and S3-compatible storage

URL: /collect/sources/amazon-s3

Read logs from an S3 bucket in AWS or AWS GovCloud through a read-only IAM role, or from MinIO, Ceph or any S3 API inside your network with a read-only access key.



If your flight logs already land in a bucket, connect it once and Foxborne reads new objects as they arrive. It hashes each object, matches it to a vehicle and never writes to the bucket.

## Before you start [#before-you-start]

* You need the **Admin** role.
* **For Amazon S3:** the bucket and prefix, and an IAM role that can list the bucket and read the prefix. Your Foxborne deployment must be able to assume the role.
* **For S3-compatible storage:** the endpoint URL, the bucket and prefix and a read-only access key.
* The Foxborne worker can reach the bucket's endpoint.

## Create a read-only IAM role [#create-a-read-only-iam-role]

Foxborne needs two permissions: list the bucket, and read the objects under the prefix. Grant nothing that writes or deletes. The connection test attempts a write and a delete, and confirms that both are refused.

Create the role in the account that owns the bucket, for example as `foxborne-read`, and allow your Foxborne deployment to assume it. Attach this permissions policy:

```json title="Permissions policy for foxborne-read" caption="Example values: bucket your-bucket, prefix px4/"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListTheBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws-us-gov:s3:::your-bucket"
    },
    {
      "Sid": "ReadThePrefix",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws-us-gov:s3:::your-bucket/px4/*"
    }
  ]
}
```

`s3:ListBucket` applies to the bucket itself, and `s3:GetObject` to the objects under the prefix. ARNs in AWS GovCloud use the `aws-us-gov` partition. In a commercial AWS account they start with `arn:aws:` instead.

If the bucket encrypts objects with a customer managed KMS key (SSE-KMS), the role also needs `kms:Decrypt` on that key. Add this statement, and check that the key policy allows the role to use the key:

```json title="Add only for SSE-KMS" caption="Example values: account 123456789012, region us-gov-west-1"
{
  "Sid": "DecryptWithTheBucketKey",
  "Effect": "Allow",
  "Action": "kms:Decrypt",
  "Resource": "arn:aws-us-gov:kms:us-gov-west-1:123456789012:key/your-key-id"
}
```

## Connect the bucket [#connect-the-bucket]

<Steps>
  <Step title="Choose Amazon S3">
    In **Sources**, select **Add source**, pick **Amazon S3** under **Object storage** and select **Continue**. The card reads &#x2A;*A bucket or prefix in your account, including GovCloud. Read through an IAM role you create.**
  </Step>

  <Step title="Fill in the fields">
    | Field                  | Example in the console                                | What to enter                                                       |
    | ---------------------- | ----------------------------------------------------- | ------------------------------------------------------------------- |
    | **Name**               | For example: Fleet log archive, west range            | A name the team will recognize.                                     |
    | **Bucket and prefix**  | `s3://your-bucket/px4/`                               | The bucket, and the prefix the logs land under.                     |
    | **IAM role to assume** | `arn:aws-us-gov:iam::123456789012:role/foxborne-read` | The ARN of the role you created.                                    |
    | **File pattern**       | `{vehicle}/{yyyy-mm-dd}/*.ulg`                        | Which objects to read, and where the vehicle ID sits in their path. |
  </Step>

  <Step title="Test the connection">
    Select **Test connection** and read the four checks as they appear:

    | Check                    | Result                                             |
    | ------------------------ | -------------------------------------------------- |
    | **Resolve and connect.** | **Reached the location from the Foxborne worker.** |
    | **List objects.**        | **Read access confirmed.**                         |
    | **Attempt a write.**     | **Refused, as it should be.**                      |
    | **Attempt a delete.**    | **Refused, as it should be.**                      |
  </Step>

  <Step title="Add the source">
    Select **Add source**. The toast names the source and says &#x2A;*The first sync is running.**
  </Step>
</Steps>

### File patterns [#file-patterns]

The example dataset's **Fleet log archive** reads `s3://hfr-flight-logs/px4/` with the pattern `{vehicle}/{yyyy-mm-dd}/*.ulg`. Under the prefix sits a folder per vehicle, then a folder per day, with the flight logs inside. `{vehicle}` marks the part of the path that names the vehicle, and Foxborne uses it to match each file.

The layout echoes the flight controller's own. PX4 writes each log to its SD card in a folder named for the date, as in `log/2026-09-24/14_13_19.ulg`, the flight log of run R-0931.

## S3-compatible storage [#s3-compatible-storage]

For MinIO, Ceph or any other S3 API inside your network, pick **S3-compatible storage** instead: &#x2A;*MinIO, Ceph or any S3 API inside your network. Read-only access key.** It reads the bucket with an access key rather than an IAM role.

| Field                 | Example in the console        | What to enter                                                                |
| --------------------- | ----------------------------- | ---------------------------------------------------------------------------- |
| **Endpoint**          | `https://minio.internal:9000` | The store's S3 endpoint inside your network.                                 |
| **Bucket and prefix** | `flight-logs/px4/`            | The bucket and the prefix the logs land under.                               |
| **Access key ID**     | `foxborne-read`               | The ID of a key that can list the bucket and read objects, and nothing more. |

Create the key in your store's own access control, limited to listing the bucket and reading objects under the prefix. The same four checks then run, and the last two confirm that writes and deletes are refused.

## Check the result [#check-the-result]

* The source appears in **Connections** with **Read-only, verified** under **Access** and **Healthy** under **Status**.
* For an Amazon S3 source, the drawer shows the bucket under **Location**, the role ARN under **Credential** and your pattern under **Matches**.
* The audit log records **Added source** with a detail such as **Amazon S3, read-only test passed**.
* As objects arrive, **Today** counts them. Runs built from the source name it above their files, for example &#x2A;*Fleet log archive. Hashed on arrival and stored unmodified.**

## Troubleshoot [#troubleshoot]

* **Status shows Needs attention or Error.** Open the source's drawer and read the note at the top.
* **Runs from the bucket show Partial.** The bucket holds flight logs only, so bring in the other sources through a collector, another source or **Import files**. In the example dataset, run R-0930 came from the fleet log archive and reads **No companion journal for this run**.
* **Files land on the wrong vehicle.** Check where `{vehicle}` sits in the pattern, against the real object keys.

## Next [#next]

* [Connect a storage source](/collect/sources) covers every source type, the drawer and its actions.
* [Supported file formats](/collect/formats) lists the formats a source can hold.
* [Stream logs to Foxborne](/collect/streaming) covers delivery paths beyond storage sources.
