JG Docs
Used Vehicles

AWS S3

Amazon S3 gives you full control over vehicle photo storage, public access and CDN delivery. JG Used Vehicles creates a temporary S3 upload URL, then the in-game browser uploads each photo directly to S3.

You need four parts working together:

PartWhy it matters
S3 bucketStores the vehicle photos
IAM access keySigns upload and delete requests
CORS policyLets the in-game browser upload to the bucket
Public read access or CDNLets players load the saved photo URL

The two URLs

The upload URL is temporary. JG Used Vehicles creates it on the server, then the in-game browser uses it once to upload a photo.

The public URL is saved with the photo. Players and the server use it to load the image later. It normally looks like one of these:

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

If you use CloudFront or another CDN, put its domain in publicUrl.

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 choose a name that is available.

Create an IAM user and access key

Create an IAM access key that can upload, read and delete objects inside the used-vehicles/ prefix.

AWS documentation:

Use this policy as a starting point:

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

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

Enable CORS for direct uploads

The in-game browser uploads directly to S3, so the bucket must allow browser PUT requests from jg-usedvehicles.

Open Permissions > Cross-origin resource sharing (CORS) on the bucket and use this as a starting point:

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

If you renamed the resource, replace jg-usedvehicles in AllowedOrigins.

Make uploaded photos public

The final photo URL must load without AWS credentials.

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

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

AWS may block this policy through the bucket's Block Public Access settings.

If you do not want a public bucket, put CloudFront or another CDN in front of it and use that domain for publicUrl.

AWS documentation:

Configure JG Used Vehicles

Set the provider in config/config.lua:

config/config.lua
Config.ImageStorageProvider = "s3"

Add the S3 settings in config/config.photos.lua:

config/config.photos.lua
Config.ImageStorageProviders.s3 = {
  bucket = "my-vehicle-images",
  region = "eu-west-2",
  accessKeyId = "YOUR_AWS_ACCESS_KEY_ID",
  secretAccessKey = "YOUR_AWS_SECRET_ACCESS_KEY",
  sessionToken = "",
  endpoint = "",
  publicUrl = "https://my-vehicle-images.s3.eu-west-2.amazonaws.com",
  prefix = "used-vehicles",
  acl = nil,
  forcePathStyle = false,
}

If you use CloudFront, set:

config/config.photos.lua
publicUrl = "https://cdn.example.com"

Do not include a trailing slash in publicUrl.

Test the final URL

Restart jg-usedvehicles and open the vehicle camera. Take one photo after the provider check passes, then confirm the photo appears in the photo library.

If the photo uploads but does not display, copy its URL and open it in a browser. Check the bucket policy, Block Public Access settings, CDN or publicUrl if it does not load.

Config fields

FieldRequiredDescription
bucketYesS3 bucket name.
regionYesAWS region, for example "eu-west-2".
accessKeyIdYesAWS access key ID.
secretAccessKeyYesAWS secret access key.
sessionTokenNoTemporary AWS session token when using temporary credentials.
endpointNoCustom S3-compatible endpoint. Leave empty for AWS S3.
publicUrlNoPublic URL or CDN domain. Standard S3 uses the bucket URL when this is empty.
prefixNoFolder-style prefix. Defaults to "used-vehicles" in the example config.
aclNoCanned ACL. Leave nil unless your S3 setup requires one.
forcePathStyleNoEnable only when a custom S3-compatible provider requires path-style URLs.

Troubleshooting

Last updated 26 August 2026

On this page