Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rbx place

Upload, download, and rollback Roblox place files via the Open Cloud API.

rbx place manages .rbxl files across multiple environments (prod, staging, dev) defined in a shared rbxplace.toml. It handles Team Create locks gracefully and can require confirmation before writing to sensitive environments.

Features

  • Multi-environment - Define prod, staging, dev (and any others) in a single rbxplace.toml
  • Upload - Push a .rbxl to one place or all places in an environment at once
  • Download - Fetch the latest or a specific version of a place file
  • Promote - Copy a place from one environment to another, optionally broadcasting to all target places
  • Rollback - Revert a place to a previous version with an interactive selector
  • Version history - List recent versions with published status and timestamps
  • Team Create detection - Clear error when a place is locked by an active Studio session
  • Confirmation guard - Per-environment confirm = true prompts before write operations
  • Fetch - Auto-populate rbxplace.toml from live Roblox universe
  • JSON - --json on versions, places, upload, promote and rollback writes one document to stdout and nothing else, with documented field names, for jq and CI

Generating the env module lives in rbx env gen-module, the command that owns rbxplace.toml.

Quick start

Create a rbxplace.toml at the root of your project:

[prod]
universe_id = 9876543210
confirm = true
places.main = 123456789012345

[staging]
universe_id = 9876543211
places.main = 234567890123456

[dev]
universe_id = 9876543212
places.main = 345678901234567

Then upload a build:

rbx place upload --env staging --file build.rbxl
rbx place upload --env prod --file build.rbxl   # prompts for confirmation

Commands

rbx place upload

Upload a .rbxl file to one or all places in an environment. By default, uploads are saved as drafts (not published live).

rbx place upload --env staging --file build.rbxl           # save as draft
rbx place upload --env prod --place lobby --file build.rbxl --published  # publish live
rbx place upload --env prod --all-places --file build.rbxl
FlagDescription
--envTarget environment (required)
--filePath to the .rbxl file (required)
--placePlace name to upload to (defaults to the only place if unambiguous)
--all-placesUpload to every place defined in the environment
--publishedPublish immediately (default: save as draft)
--jsonWrite the result to stdout as one JSON document instead of the progress lines

By default, uploads are saved as drafts. Use --published to publish live.

If the target place has an active Team Create session, the upload fails immediately with a clear message rather than returning a generic error.

If the environment has confirm = true, a confirmation prompt is shown before uploading.

--json

rbx place upload --env staging --file build.rbxl --json
{
  "schema_version": 1,
  "command": "upload",
  "ok": true,
  "env": "staging",
  "universe_id": "9876543211",
  "published": false,
  "place_id": "234567890123456",
  "version": "173",
  "results": [{ "place": "main", "place_id": "234567890123456", "version": "173" }]
}

The fields are shared with promote and rollback and are described once in Write documents.

--json cannot prompt, so an environment with confirm = true needs --yes; without it the command fails with a message on stderr naming the flag and writes nothing to stdout.

VERSION=$(rbx place upload --env staging --file build.rbxl --json | jq -r .version)
rbx place download

Download a place file from Roblox.

rbx place download --env prod
rbx place download --env prod --version 42 --out backup.rbxl
rbx place download --env staging --published
rbx place download --env staging --saved
FlagDescription
--envTarget environment (required unless --place-id is given)
--placePlace name (defaults to the only place if unambiguous)
--place-idA place id instead of an env and a name. Skips rbxplace.toml; global flag
--versionSpecific version number to download (default: latest)
--publishedDownload the latest published version specifically
--savedDownload the latest saved (draft) version specifically
--outOutput path (default: <place_id>.rbxl)
rbx place promote

Promote a place from one environment to another. Downloads the source place in-memory and uploads it to the target. Without --all-places, the same-named place is targeted in the destination environment.

rbx place promote --from staging --to prod                         # latest → matching place
rbx place promote --from staging --to prod --all-places            # latest → every place in prod
rbx place promote --from staging --to prod --from-published        # latest published version
rbx place promote --from dev --to staging --version 42 --published # specific version, publish live
rbx place promote --from staging --to prod --log deploy.json       # write traceability log
FlagDescription
--fromSource environment (required)
--toTarget environment (required)
--placeSource place name (defaults to the only place if unambiguous)
--all-placesUpload to every place defined in the target environment
--versionSpecific source version to promote
--from-publishedPromote the latest published version from the source
--from-savedPromote the latest saved (draft) version from the source
--publishedPublish immediately on the target (default: save as draft)
--logPath to a JSON file for traceability logging (merged, not overwritten)
--jsonWrite the result to stdout as one JSON document instead of the progress lines

If the target environment has confirm = true, a confirmation prompt is shown before uploading.

--all-places is a broadcast, not a plural

Without it, promote maps by name: the source place is resolved, and the same key is looked up in the target env. main goes to main, lobby goes to lobby, and a target env missing that name is an error. That is the default and it is what people usually mean.

--all-places does something else. Every place in the target env receives the same bytes, downloaded once from the single source place:

# prod's main, lobby and arena all become copies of staging's main
rbx place promote --from staging --to prod --all-places

There is no name matching in that path. It is occasionally what you want — several places that really are the same file — and it is unrecoverable when it is not: each target gets a new version, those version numbers are real, and undoing it is one rollback per place.

So the confirmation says it outright rather than only listing the targets:

⚠ Promote staging/main v172 → prod (arena, lobby, main)? This will save as draft.
  Every one of them is overwritten with staging/main, not with its own counterpart.

If what you want is “promote every place to its same-named counterpart”, that is a different operation and this flag is not it. Run promote once per place, or leave the flag off and let the name mapping do it.

When --log is provided, a JSON file is written (or updated) after a successful promote. Only the promoted places are updated in the file; other entries are preserved:

{
  "main": {
    "deployedAt": "2026-05-14T15:30:00+01:00",
    "staging": { "universeId": 9876543210, "placeId": 123456789012345, "version": 172 },
    "production": { "universeId": 9876543211, "placeId": 234567890123456, "version": 27 }
  }
}

--json

The same information --log files away, on stdout, without a file. --log is still honored when both are given; the “Log written” line moves to stderr so the document stays parsable.

rbx place promote --from staging --to prod --from-published --published --yes --json
{
  "schema_version": 1,
  "command": "promote",
  "ok": true,
  "env": "prod",
  "from_env": "staging",
  "universe_id": "9876543211",
  "published": true,
  "source_place": "main",
  "source_place_id": "123456789012345",
  "source_version": "172",
  "place_id": "234567890123456",
  "version": "27",
  "results": [{ "place": "main", "place_id": "234567890123456", "version": "27" }]
}

source_version is the version that was actually promoted, resolved before anything is downloaded, so --from-published and a bare latest both report the number they picked rather than the flag that picked it. See Write documents for the rest of the fields.

rbx place rollback

Roll back a place to a previous version. Without --version, shows an interactive selector with recent versions.

rbx place rollback --env prod               # interactive selector
rbx place rollback --env prod --version 42  # direct rollback
rbx place rollback --env prod --count 20    # show 20 versions in selector
FlagDescription
--envTarget environment (required)
--placePlace name (defaults to the only place if unambiguous)
--versionVersion to roll back to (skips interactive selector)
--countNumber of recent versions to show in selector (default: 10)
--jsonWrite the result to stdout as one JSON document instead of the progress lines

Rollback creates a new version on Roblox (it does not modify history). If the place is locked by Team Create, the operation fails immediately with a clear message.

If the environment has confirm = true, a confirmation prompt is shown before rolling back.

--json

rbx place rollback --env prod --version 37 --yes --json
{
  "schema_version": 1,
  "command": "rollback",
  "ok": true,
  "env": "prod",
  "universe_id": "9876543210",
  "published": true,
  "source_version": "37",
  "place_id": "123456789012345",
  "version": "44",
  "results": [{ "place": "main", "place_id": "123456789012345", "version": "44" }]
}

Both versions are reported: source_version is the one restored, version is the new one Roblox created for it. published is always true, because rolling back republishes live.

--version is required under --json: the interactive selector is a prompt, and --json cannot prompt. Without it the command fails before fetching anything, with a message on stderr naming the flag.

rbx place versions

List recent versions of a place.

rbx place versions --env prod
rbx place versions --env staging --count 50
rbx place versions --env prod --filter published
rbx place versions --env prod --filter saved
FlagDescription
--envTarget environment (required)
--placePlace name (defaults to the only place if unambiguous)
--countNumber of versions to show (default: 20, or 3 when --filter is published/saved)
--filterFilter by version type: all (default), published, or saved
--jsonWrite the versions to stdout as one JSON document instead of the listing

--json

One JSON document on stdout, nothing else. Diagnostics, the unknown-key warning in particular, stay on stderr, so the document parses even when rbxplace.toml has something wrong with it.

{
  "schema_version": 1,
  "env": "prod",
  "place": "main",
  "place_id": "123456789012345",
  "filter": "all",
  "count": 20,
  "count_reached": false,
  "versions": [
    { "version": "173", "published": true, "create_time": "2024-01-15T14:30:00Z" },
    { "version": "172", "published": false, "create_time": "2024-01-14T09:02:00Z" }
  ]
}
FieldTypeMeaning
schema_versionintegerDocument format, shared with rbx check --json. 1 today. Refuse a version you do not understand
envstringThe environment asked for
placestringThe rbxplace.toml place name, after the --place defaulting rule
place_idstringThe place id, as a string
filterstringall, published, or saved: the --filter in force
countintegerThe --count in force. A maximum, not a promise
count_reachedbooleanTrue when the walk stopped at --count rather than running out of versions. Raise --count to see the rest
versionsarray of objectsNewest first, the order the listing prints
versions[].versionstringThe version number. --version takes it back verbatim
versions[].publishedbooleanLive, as opposed to a saved draft
versions[].create_timestringExactly what Roblox sent, RFC 3339. The listing rewrites this into 2024-01-15 14:30:00 UTC; that is a rendering, and the document keeps the original

A place with no versions is an empty versions array and exit 0, not an error: a consumer reads a zero off it.

rbx place versions --env prod --json | jq -r '.versions[] | select(.published) | .version' | head -1
rbx place places

List all places in a universe. Shows which places are configured vs missing from rbxplace.toml if using --env.

rbx place places --env prod                    # list places, show which are in toml
rbx place places --universe-id 9876543210     # list places without config
FlagDescription
--envEnvironment name (reads universe from toml, shows config status)
--universe-idUniverse ID override (one-shot listing without toml)
--jsonWrite the places to stdout as one JSON document instead of the listing

--json

{
  "schema_version": 1,
  "env": "prod",
  "universe_id": "9876543210",
  "places": [
    {
      "place_id": "123456789012345",
      "display_name": "Main Place",
      "max_player_count": 50,
      "place": "main",
      "configured": true
    },
    { "place_id": "987654321", "display_name": "Test Arena", "configured": false }
  ]
}
FieldTypeMeaning
schema_versionintegerDocument format, shared with rbx check --json. 1 today
envstringThe environment whose entry named the universe. Absent under a bare --universe-id
universe_idstringThe universe listed, as a string
placesarray of objectsOne per place Roblox reports, in the order it returned them
places[].place_idstringThe place id. Absent when Roblox returned a path it could not be read out of, which the listing renders as ?
places[].display_namestringThe name Roblox shows, which is not the rbxplace.toml key
places[].max_player_countintegerAbsent when Roblox did not report one
places[].placestringThe rbxplace.toml key this place is mapped to, the name --place takes. Absent when the file does not have it
places[].configuredbooleanWhether the file has this place, the fact the listing marks NOT in toml. Absent, rather than false, under a bare --universe-id: with no config in play the question has no answer
rbx place places --env prod --json | jq -r '.places[] | select(.configured | not) | .place_id'
rbx place fetch

Fetch all places from a universe and update rbxplace.toml. Existing place keys are preserved where the ID already matches. New places get keys generated from their Roblox display names.

rbx place fetch --env prod              # dry-run: shows what would be written
rbx place fetch --env prod --write      # writes to rbxplace.toml
rbx place fetch --env prod --universe-id 9876543210 --write  # override universe
FlagDescription
--envEnvironment section to update in rbxplace.toml (required)
--universe-idUniverse ID override (uses rbxplace.toml value if omitted)
--writeWrite changes to rbxplace.toml (default: dry-run)

Write documents

upload, promote, and rollback share one --json envelope. It is a receipt: it reports what was written, in the order it was written, with the version number Roblox assigned to each place.

FieldTypeMeaning
schema_versionintegerDocument format, shared with rbx check --json. 1 today
commandstringupload, promote, or rollback, so a mixed stream of receipts can be dispatched on
okbooleanFalse when a target failed. The exit code says the same thing; this is here so a consumer that captured stdout does not have to plumb $? through as well
envstringThe environment written to. For promote, the target
from_envstringThe source environment of a promote. Absent otherwise
universe_idstringThe universe written to
publishedbooleanWhether the new versions are live. Always true for rollback
source_place / source_place_idstringWhere a promote read its bytes, after --place defaulting. Absent otherwise
source_versionstringThe version the new one was made from: the promoted source version, or the version rolled back to. Absent for upload, whose source is a local file
place_id / versionstringThe single-target shortcut: the place written and the version it received. Absent under --all-places, and absent when nothing was written
resultsarray of objectsOne entry per place that got a new version, in write order. Empty when the first target failed
results[].placestringThe rbxplace.toml key
results[].place_idstringThe place id
results[].versionstringThe version Roblox assigned to this write
errorstringWhy the run stopped. Absent when ok is true. The same text is on stderr, where it is the process’s error message

Three rules are worth stating outright, because scripts depend on them:

A run that fails partway still emits a document. place upload --all-places can write two places and then hit a Team Create lock on the third. Those two versions exist and cannot be taken back, so results reports them, ok is false, error says what stopped it, and the process still exits non-zero. A deploy log that loses a write that happened is worse than no log.

A failure before the first write, on the other hand, writes nothing to stdout at all: an unknown environment, a refused confirmation, a source version that does not exist. Nothing happened, and an empty stdout next to a non-zero exit says so without ambiguity.

The shape follows the invocation, never the data. A single-target run fills the place_id and version shortcuts next to results; an --all-places run fills results only, even against an environment with exactly one place. This is the rule rbx env get --json uses for value, and it exists so a filter cannot start working by accident and break when a second place is added.

Ids and version numbers are strings. They identify an asset rather than count anything, a place id exceeds 2^53, and a consumer that parses them as JSON numbers would round them. Keeping versions in the same form means the output of one command feeds the input of the next without a conversion:

VERSION=$(rbx place upload --env prod --file build.rbxl --published --yes --json | jq -r .version)
rbx servers list --env prod --version "$VERSION" --json

--json never prompts. Every write here has a point where it would stop and ask, and under --json that question fails instead, with a message on stderr naming the flag that answers it: --yes for a confirm = true environment, --version for the rollback selector.

Configuration

rbx place reads rbxplace.toml in the working directory (override with the global --places <path>). You can configure place IDs manually or use rbx place fetch to auto-populate them from a Roblox universe.

[prod]
universe_id = 9876543210
confirm = true                  # prompt before upload or rollback
places.main   = 123456789012345
places.lobby  = 987654321

[staging]
universe_id = 9876543211
places.main = 234567890123456

[dev]
universe_id = 9876543212
places.main = 345678901234567
Environment fields
FieldTypeRequiredDescription
universe_idu64YesRoblox universe ID
envstringNoEnvironment type name for code generation (defaults to section name)
confirmboolNoRequire confirmation before write operations (default: false)
places.<name>u64NoPlace ID mapped to a name

The rbxplace.toml file is shared with every other subcommand: they all resolve environment names to universe IDs from it.

Working without rbxplace.toml

The global --place-id names a place directly, the way --universe-id names a universe, and skips the config file. It reaches the reads:

rbx place versions --place-id 123456789012345
rbx place download --place-id 123456789012345 --out backup.rbxl

The writes refuse it, with a message saying why:

`--place-id` names a place but no env, and `rbx place upload` needs one: the confirm
guard and the --json receipt are both env-scoped. Pass --env <name> ...

Two things are genuinely env-scoped and neither survives an id on its own. confirm = true is declared on an env, so an env-less write would walk past a guard somebody set on purpose. And the --json receipt carries env as a documented field, so an env-less write would emit a document missing something consumers were told to expect. Refusing beats either, and beats accepting the flag and ignoring it.

Required API scopes

OperationScopeNotes
Upload / Promote (write)universe-places:write
Download / Promote (read)legacy-asset:manage
Version listasset:readAlso used by --from-published, --from-saved, --published, --saved
Rollbackasset:write
List placesuniverse:readFor places command