JG Docs
Vehicle Studio

AWS S3

S3 from Amazon Web Services (AWS) is the most flexible option, but it is also the easiest to misconfigure. Vehicle Studio signs a short-lived S3 PUT URL, then the NUI uploads the image directly to S3.

You need four things to work at the same time:

StepWhat you are setting upWhy it matters
1BucketThe place images are stored
2IAM access keyLets the server create presigned upload URLs
3CORSLets the NUI upload directly from the browser
4Public read access or CDNLets browsers load the saved image URL

The Two URLs To Understand

S3 has an upload URL and a public URL.

The upload URL is a short-lived presigned URL. Vehicle Studio creates it on the server, then the NUI uses it once to upload the image directly to S3.

The public URL is the final URL saved for the image. This is what players' browsers and other resources load later. In most setups it looks like one of these:

https://my-vehicle-images.s3.eu-west-2.amazonaws.com
https://cdn.example.com

If you are using CloudFront or another CDN, put the CDN domain in publicUrl.

Vehicle Studio appends a small v= query parameter to saved image URLs each time an image is retaken. The object path stays the same, but browsers are forced to fetch the newest version.

Create An S3 Bucket

  1. Open the AWS S3 console.
  2. Create a bucket, for example my-vehicle-images.
  3. Pick the AWS region closest to your server, for example eu-west-2.
  4. Save the bucket name and region.

Bucket names are globally unique, so yours must be different from everyone else's.

Create An IAM User Or Access Key

Vehicle Studio needs an access key ID and secret access key that can create presigned PUT URLs for the bucket.

AWS documentation:

This example allows uploads and reads inside the vehicle-studio/ folder only:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::my-vehicle-images/vehicle-studio/*"
    }
  ]
}

Replace my-vehicle-images with your bucket name. Replace vehicle-studio/ if you use a different prefix.

Enable CORS For Direct Uploads

Because the NUI uploads directly to S3, the bucket must allow browser PUT requests from your resource.

In the S3 bucket, open Permissions > Cross-origin resource sharing (CORS) and use this as a starting point:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["PUT"],
    "AllowedOrigins": ["https://cfx-nui-jg-vehiclestudio"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]

If you renamed the resource, replace jg-vehiclestudio in the origin. For quick testing, you can temporarily use "*" as the allowed origin, then tighten it afterwards.

Make Uploaded Images Public

This step controls whether the final image URL can actually be loaded in a browser.

For a simple public bucket setup, add a bucket policy like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadVehicleStudioImages",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-vehicle-images/vehicle-studio/*"
    }
  ]
}

If AWS blocks this policy, check the bucket's Block Public Access settings. AWS blocks public bucket policies by default in many setups.

If you do not want a public bucket, use CloudFront or another CDN in front of the bucket instead. In that case, set publicUrl to the CDN domain.

AWS documentation:

Configure Vehicle Studio

In config/config.lua:

Config.ImageStorageProvider = "s3"

In config/config.upload.lua:

Config.ImageStorageProviders = Config.ImageStorageProviders or {}

Config.ImageStorageProviders.s3 = {
  bucket = "my-vehicle-images",
  region = "eu-west-2",
  accessKeyId = "YOUR_AWS_ACCESS_KEY_ID",
  secretAccessKey = "YOUR_AWS_SECRET_ACCESS_KEY",
  publicUrl = "https://my-vehicle-images.s3.eu-west-2.amazonaws.com",
  prefix = "vehicle-studio",
  acl = nil,
  endpoint = nil,
  forcePathStyle = false,
  presignExpires = 900
}

If you use CloudFront:

publicUrl = "https://cdn.example.com"

If you use a custom S3-compatible endpoint, set endpoint. Normal AWS S3 does not need it.

Test The Final URL

After restarting the resource, generate one image and open the saved image URL in a browser.

For example:

https://my-vehicle-images.s3.eu-west-2.amazonaws.com/vehicle-studio/adder.webp

If that URL does not open publicly, the issue is usually the bucket policy, Block Public Access settings, CDN setup, or an incorrect publicUrl.

Config Fields

FieldRequiredDescription
bucketYesS3 bucket name.
regionYesAWS region, for example "eu-west-2".
accessKeyIdYesAWS access key ID.
secretAccessKeyYesAWS secret access key.
publicUrlYesPublic read URL used to build the final saved image URL.
prefixNoFolder-style prefix for uploaded images, for example "vehicle-studio".
aclNoOptional canned ACL. Leave nil unless you know the provider requires it.
endpointNoOptional custom S3-compatible endpoint. Normal AWS S3 does not need this.
forcePathStyleNoOptional for S3-compatible providers.
presignExpiresNoPresigned upload URL lifetime in seconds. Defaults to 900.

Troubleshooting

On this page