A save that lives on a server follows the player across devices. Both managers here can also keep the game playable with no connection, through an optional offline cache.

Remote means asynchronous

A remote load has to reach a server, so it cannot be synchronous: remote managers load asynchronously. Use LoadAsync() / await WhenReady rather than LoadSync() (which reports it cannot run synchronously). With the offline cache on, a save can complete synchronously (it lands in the cache and pushes in the background), but a load still goes to the server.

Unlike the local managers, where the data is ready before your code runs, readiness is a real state here: gate once on await WhenReady in your boot flow (the general contract is in Core Concepts). The remote-specific effect: until the player is authenticated or the server is reachable, the automatic load keeps retrying and IsReady stays false, so a save never clobbers data a retry could still fetch.

The offline cache

Both remote managers can persist through a local cache, set with Cache Mode:

  • No Cache: every load and save reaches the server; a load fails while it is unreachable.
  • Server + Cache: the cache keeps the game playable offline, but a load still goes to the server whenever it is reachable (falling back to the cache only when it is not), so it picks up saves made on other devices; a conflict between the two is settled by the most recent save (this relies on device clocks).
  • Local Cache (Server Backup): the cache is the source of truth and the local save always wins; the server is a write-only backup, read only when nothing is cached, such as after a reinstall. This gives you a cloud backup and offline play rather than live cross-device sync.

With a cache on, a save always lands locally first and succeeds even with no connection, then a background push loop reconciles it with the server whenever the server is reachable. Data a previous session could not push starts uploading as soon as the manager is in scope; marking the target IAlwaysGlobal guarantees that starts at game launch.

When nothing is cached and the server cannot be reached, Offline Cold Start decides what a load does:

  • Wait For Server (recommended): keep retrying until reachable, so a reinstalled device restores its backup instead of starting fresh over it (a brand-new offline player cannot start until reachable).
  • Start Fresh: start on default data so a new player can play offline, at the cost of that protection.
YOUR GAME save ON DEVICE Local cache REMOTE Server write instant, durable push (background) retries until confirmed
A save is durable locally first, then pushed in the background
The push retry cadence is configured project-wide in Project Settings > Persistent Asset > Remote.

A "saving / saved" indicator

With a cache on, Status tells you whether everything has reached the server (UpToDate) or local changes are still waiting (PendingChanges), and StatusChanged fires when it flips, perfect for a cloud icon:

remoteManager.StatusChanged += status =>
    cloudIcon.SetActive(status == RemoteStatus.PendingChanges);

To flush now instead of waiting for the background retry (for example before sign-out), call PushPendingChanges() and await the resulting status.

Server (HTTP)

Server (HTTP) is a built-in persistence manager that saves to your own server over HTTP. Each save lives at its own address, url/slot/id: the configured URL, the slot when there is one, and the save's id.

Server (HTTP) manager inspector
Server (HTTP): point it at your endpoint
  • Release Url / Development Url: the endpoint, with an optional override used in the editor and development builds (a local or staging server).
  • Id: override the last part of the address (defaults to the manager's unique id).
  • Use Post Requests: send saves and clears as POST with an X-HTTP-Method-Override header, for hosts that refuse PUT and DELETE.
  • Compression: gzip the body before sending (the server must store and return it verbatim).

The server only needs three routes:

  • GET url/slot/id: return the stored body (404 when there is none).
  • PUT url/slot/id: store the request body verbatim.
  • DELETE url/slot/id: remove it.

For authenticated servers, provide credentials from code at runtime:

// Sent as the Authorization header of every request
HttpPersistenceManager.Authorization = "Bearer " + token;

// Or adjust each request yourself (api key, signed query, extra headers)
HttpPersistenceManager.ConfigureRequest = request =>
    request.SetRequestHeader("X-Api-Key", apiKey);
Verbatim storage matters: a server that trims whitespace or re-encodes the body breaks loading. The body must come back byte for byte.

Cloud Save (UGS)

Cloud Save (UGS) is a built-in persistence manager that saves to Unity Gaming Services Cloud Save, per signed-in player. Each save lives under a key in that player's Cloud Save data.

Cloud Save (UGS) manager inspector
Cloud Save (UGS): per-player cloud storage
  • Key: the storage key (defaults to the manager's unique id). Cloud Save keys allow only letters, digits, - and _.
  • Compression: gzip (and base64, since Cloud Save stores a string) to fit larger saves under the value-size limit.
Cloud Save is per player, so your game must initialize Unity Services and sign a player in (UGS Authentication) before the target loads. The manager does not do this for you; until a player is signed in, the load retries.
Requires the UGS Cloud Save and Authentication packages in your project.

Custom managers

Talking to a different remote (another REST contract, a third-party service, PlayFab, a custom socket protocol) is just a custom persistence manager: override the async read, write and clear methods, the same as any other manager. If you also want an offline cache, a background push loop and conflict handling, the RemotePersistenceManager base class hands you all of that for three primitives (FetchRemote, StoreRemote, DeleteRemote). It is an optional convenience, not the required path: a simple always-online service is often cleaner as a plain manager. See Extending.