guides

Bring your own storage (BYO S3-compatible bucket)

TRaX can use your S3-compatible bucket — AWS S3, Cloudflare R2, Backblaze B2, self-hosted MinIO — as a media source and as the destination for cloud recordings. Your files stay in your bucket, under your keys, under your retention rules. TRaX accesses the bucket server-side only: credentials are sent write-only, encrypted at rest, never echoed back (you'll only ever see the last 4 characters of the access key), and never reach the browser.

Connecting a bucket, updating it, verifying it, and importing files from it require an active subscription. Browsing your existing configs and removing them always works.

Per-provider recipes (exact endpoint / region / key formats) are in BYO storage: provider recipes.

Where to set it up

  • Studio Settings → Storage — add, edit, re-verify, remove buckets.
  • Media library → Add storage — the same create form, inline, so you can connect a bucket and browse it without leaving the picker.

Only the studio owner can manage external storage.

The form fields

Field Required What it is
Name yes A label for you ("My R2 bucket").
Endpoint yes The S3 API endpoint, e.g. https://s3.us-east-1.amazonaws.com. Bare host or https:// URL — no path, no query. http:// is rejected.
Region no The bucket's region, used for request signing. Leave blank only if your provider ignores region (blank is signed as us-east-1).
Bucket yes The bucket name.
Prefix no A key prefix that jails everything TRaX does — browsing, imports, and recordings all stay under it. Recommended: give TRaX its own prefix (e.g. trax/).
Force path-style no (default on) Addresses objects as endpoint/bucket/key instead of bucket.endpoint/key. Required for MinIO and most self-hosted setups; harmless for AWS/R2/B2.
Access key ID yes Sent write-only. Shown later only as •••• + last 4 characters.
Secret access key yes Sent write-only, never displayed again.

To rotate credentials, edit the config and enter both keys again — leaving them blank keeps the existing pair. A lone access key without its secret is rejected.

Endpoint requirements (why some endpoints are rejected)

TRaX dials your endpoint from its servers, so endpoints get the same network hardening as any user-supplied URL:

  • HTTPS only. Plaintext http:// endpoints are rejected.
  • Publicly resolvable and publicly routable. Private, loopback, link-local, and carrier-NAT IP addresses are rejected — both as literal IPs in the endpoint and at connection time if a DNS name resolves to one.
  • Fully-qualified hostnames only. Bare single-label names (minio, nas) are rejected.

Practical consequence: a self-hosted MinIO (or similar) must be reachable from the internet on a public DNS name with a valid TLS certificate. A bucket that only exists on your LAN or behind a VPN cannot be connected. See the MinIO recipe.

Minimal IAM policy

Scope TRaX's key to one bucket (and one prefix, if you set one). This is the complete set of permissions TRaX uses — nothing else is needed:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "TraxObjects",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:AbortMultipartUpload"
      ],
      "Resource": "arn:aws:s3:::YOUR-BUCKET/trax/*"
    },
    {
      "Sid": "TraxList",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::YOUR-BUCKET",
      "Condition": { "StringLike": { "s3:prefix": "trax/*" } }
    }
  ]
}

If you don't use a prefix, change the object resource to arn:aws:s3:::YOUR-BUCKET/* and drop the Condition.

What each permission is for:

Permission Used for
s3:GetObject Reading files you import (playback, thumbnails, going live with them) and reading recording segments during processing.
s3:PutObject The verification probe, recording segment uploads, processed recording output, and files you move into the bucket.
s3:DeleteObject Cleaning up the tiny verification probe object, and the delete half of an explicit "Move". Never used on your recordings or library files otherwise.
s3:AbortMultipartUpload Cleaning up interrupted large streaming uploads during recording processing.
s3:ListBucket Browsing the bucket in the media picker.

A read-only key (s3:GetObject + s3:ListBucket) also works: the config verifies as Forbidden with the note "usable as a read-only library" — you can browse and import, but not record or move files in.

Your provider must support S3 multipart upload

Anything large TRaX writes into your bucket goes up as an S3 multipart upload: recording segments, the processed recording output, and files copied in from the TRaX library. Your provider has to implement the multipart operations the way the S3 API specifies them:

  • CreateMultipartUpload
  • UploadPart
  • CompleteMultipartUpload
  • AbortMultipartUpload
  • ListMultipartUploads

AWS S3, Cloudflare R2, Backblaze B2 and MinIO all do. A cut-down or homegrown S3 façade may not, and it will fail on the first file too big for a single request rather than at save time: the verification probe writes one small object, so it cannot tell you whether multipart works.

s3:AbortMultipartUpload is in the IAM policy above for the same reason. Without it, an upload interrupted halfway leaves parts sitting in your bucket that nothing can clean up, and most providers bill for them.

CORS: not required, but expose ETag

You do not need CORS to connect a bucket. Every operation that needs your credentials runs server-side, and the browser only ever consumes short-lived presigned links through regular media playback and download — neither of which is subject to CORS.

If your bucket has a CORS policy, the one thing worth adding is ETag in ExposeHeaders:

"ExposeHeaders": ["ETag"]

A browser uploading in parts reads each part's ETag off the PUT response and sends the list back to finish the upload. A bucket that hides ETag hands it null instead. That is recommended, not required — TRaX recovers the part list server-side with ListObjectParts when the browser can't report it, so the upload still completes either way. Exposing the header just saves the extra round trip, and it is the only response header any browser-side S3 flow needs.

The verification probe

On every save (and whenever you hit Re-verify) TRaX runs a real round-trip against your bucket, bounded at 10 seconds:

  1. PUT a small marker object at <prefix>/._trax/probe-<random-id>
  2. GET it back and compare the bytes
  3. DELETE it

A failed probe still saves the config — it just gets a non-verified status pill so you can fix the policy/DNS/keys and hit Re-verify.

Status pills

Pill Meaning What to do
Verified Probe succeeded. Everything works, including recording. Nothing.
Unverified Saved but not yet successfully probed. Hit Re-verify.
Unreachable Endpoint/bucket couldn't be reached, doesn't exist, or the request was rejected before auth. See the troubleshooting table below.
Invalid credentials The provider rejected the key (unknown key ID or bad signature). Re-enter both keys; check for copy/paste whitespace.
Forbidden The key authenticated but was denied. If reads work, the pill notes the bucket is usable as a read-only library. Widen the key's policy (see the IAM policy above) if you want recording/move.

Statuses are event-driven: a real operation failing later (a browse, an import) flips the pill; the next successful operation self-heals it back to Verified. Re-verify re-runs the full probe on demand — the pill's tooltip carries the exact provider error.

Troubleshooting

Symptom Likely cause Fix
Unreachable, detail mentions a region or AuthorizationHeaderMalformed Region field doesn't match the bucket's real region (blank = signed as us-east-1). Set the Region field to the bucket's region (AWS), or auto (R2), or the region embedded in the endpoint (B2).
Unreachable, NoSuchBucket Bucket name typo — or path-style mismatch making the provider look up the wrong name. Check the bucket name; toggle Force path-style to match your provider (on for MinIO, fine either way for AWS/R2/B2).
Invalid credentials, SignatureDoesNotMatch Wrong secret key — or (self-hosted) the storage server's clock is skewed, which breaks request signing. Re-enter both keys. On self-hosted servers, sync the clock (NTP).
"endpoint host … is not allowed" when saving The endpoint is a private/LAN address, a bare hostname, or http://. Use a public HTTPS endpoint — see endpoint requirements.
Unreachable, "bucket unreachable — check endpoint, region, and network" DNS failure, TLS failure, timeout — or a public DNS name that resolves to a private IP (blocked at connect time). Verify the endpoint resolves publicly and serves valid TLS; check firewall allows inbound from the internet.
Forbidden on save, but browsing works Key has read permissions only. Fine for a media library. Add write permissions for recording — see the IAM policy.

Importing and moving files

  • Browsing is live — the picker lists your bucket directly (media file types only: mp4, webm, mov, jpg/jpeg, png, webp, gif, mp3, wav, ogg, aac; up to 5 GiB per file). Nothing is copied or mirrored.
  • Importing a file registers it in your TRaX media library — the bytes stay in your bucket. Deleting an imported item from the library never deletes the object in your bucket.
  • There is no direct upload into your bucket through TRaX yet. To get a new file into your bucket, either upload it there yourself with your own tools and import it, or upload it to the TRaX library and use Move/Copy in the media library to transfer it across. The library upload takes large files without a size surprise — it splits them into parts and shows byte-level progress — up to your library quota, and the Move/Copy across to your bucket then runs server-side. Once the file is in your bucket it no longer counts against that quota.

Removing a config

Removing a bucket config never touches the bucket's contents. If imported library items still reference it, TRaX warns you how many will stop resolving and asks you to confirm.

Next