Shipping Live Content Packs with Unity Addressables
A concrete Addressables setup for indie teams that need to ship seasonal props, balance tables, and locale packs without rebuilding the whole client every Tuesday.

Most indie Unity projects discover remote content the hard way: a festival skin lands in the build, the binary balloons, and TestFlight still rejects the upload. Addressables will not invent your live-ops calendar, but it will let you treat downloadable packs as first-class citizens instead of a zip glued onto StreamingAssets.
This article walks through one concrete pattern for a small team shipping seasonal props, balance tables, and optional locale packs. The goal is a boring, repeatable pipeline—not a tour of every Addressables window.
What “live content” means here
Scope the problem before you open the Groups window. For this walkthrough, live content means:
- Cosmetic props and materials that never gate progression
- ScriptableObject balance tables that designers tweak weekly
- Optional locale string tables for a second language
It does not mean replacing your entire level streaming system or hot-patching C# assemblies. Keep code in the player build. Ship data and art remotely.
Write those boundaries on a sticky note. Addressables can host almost anything; shipping discipline starts with saying no.
Catalog the packs before you catalog the assets
Name packs by purpose, not by folder:
cosmetics_season_01balance_v42locale_pt_br
Each pack becomes one Addressables group with a clear remote load path. Artists drop assets into the group; the build script produces a content catalog and bundle set that your bootstrap scene can request by label.
Avoid a single “everything remote” group. Failure modes get louder when a 2 MB balance table shares a bundle with a 180 MB prop set.
Labels beat long address strings
Use short, stable addresses for individual assets if you must, but drive loading with labels:
- Label
season_01on every cosmetic in that drop - Label
balanceon the current balance ScriptableObject - Label
locale_pt_bron the Portuguese string table
Your runtime code then asks for a label, waits for download if needed, and instantiates or applies the result. When season 02 ships, you add assets and a new label—you do not rewrite every LoadAssetAsync call site.
Keep label names boring and version-aware when content is mutually exclusive (balance_active pointing at one SO at a time).
Remote paths that survive a CDN move
Point Remote.LoadPath at a profile variable, not a hard-coded URL:
https://cdn.example.com/game/{BuildTarget}/{CatalogVersion}
Profiles for Local, Staging, and Production should differ only in host and catalog version. Build Addressables once per platform, upload the ServerData output to the matching CDN prefix, and promote by copying or flipping a DNS/path—not by rebuilding Unity.
Document who owns the upload step. If “whoever has the AWS key that day” is the answer, fix that before launch week.
A minimal bootstrap flow
In the first interactive scene (or a dedicated boot scene):
- Initialize Addressables.
- Check for catalog updates if you enable that path.
- Load the
balancelabel and apply it to your gameplay services. - Kick optional packs (
locale_*,season_*) in the background with a small progress UI.
Do not block the main menu on cosmetics. Do block combat entry on balance if cheating or softlocks are possible when tables are missing.
Handle failures explicitly: offline mode with last-good cached packs, a retry button, and a clear “content unavailable” state. Silent empty references are worse than a loud error.
Build checklist that actually gets run
Before every content drop:
- Switch to the correct Addressables profile.
- Run Build → New Build → Default Build Script (or your custom build script).
- Confirm Remote.BuildPath output contains the new bundles and catalog hash.
- Upload only the changed bundles plus the new catalog (or the full set if your CDN strategy prefers replace-all).
- Smoke-test on a clean device install and on an install that still has the previous catalog cached.
Automate steps 2–4 in CI when you can. Manual uploads are fine for a three-person team until the second person ships a pack from the wrong profile.
Memory and dependency traps
Addressables will pull dependencies. A material that references a giant atlas can yank an entire texture set into a “tiny” cosmetic pack. Review dependencies in the Analyze window before you ship.
Unload aggressively when leaving a seasonal event. Release what you loaded; do not assume scene unload cleans remote handles for you. Track handles in a small content service instead of scattering AsyncOperationHandle fields across UI scripts.
For ScriptableObjects, prefer immutable data snapshots loaded once per session over reloading mid-match unless you have a clear hot-reload protocol.
Local iteration without lying to yourself
Use the Use Asset Database play mode for day-to-day work so artists iterate without building bundles every five minutes. Once a week—or before every remote drop—switch to Use Existing Build against Staging so you exercise the real download path.
Nothing replaces a clean-install test on the lowest-spec device you claim to support. Catalog updates that look fine on a developer machine fail in the wild when residual caches disagree.
What not to put in remote packs
Keep these in the base build:
- Critical shaders and first-boot UI
- Anti-cheat or entitlement checks
- Tutorial levels required for a store review playthrough
- Anything that must work with zero network on first launch
Remote packs shine when they are optional, replaceable, and versioned. They fail when they become the only path to a mandatory feature.
Closing
Addressables is a delivery system, not a design doc. Name packs by purpose, load by label, keep Remote.LoadPath in profiles, and run a boring checklist before every CDN upload. Do that, and Tuesday’s balance tweak stops being a full client rebuild—and your festival props stop hitchhiking inside the binary.

