An AI agent that keeps state across turns has different deployment needs from an API endpoint that treats each request as fresh. The state is the point. If the host wipes it on a redeploy, scale-to-zero drops it, or the volume unmounts on restart, the agent forgets what it was doing and re-pays for tool calls and model output it already produced. Fly.io, Railway, and Suga each handle that state differently.
The state an agent accumulates
Conversation history is the first piece. Each user turn appends to a transcript, and dropping the transcript means starting from scratch. Tool result caches come next: an agent that calls an expensive tool with the same arguments a second time should reuse the answer, so anywhere from a few kilobytes to a few gigabytes of cached results tends to accumulate. Retrieval-augmented agents keep embeddings, either in a dedicated vector store or in a local index file. Long-running workflows checkpoint themselves between steps so a restart does not redo work already done. Many agents just use SQLite for all of it, mounted on a local path and treated as the source of truth.
This state is expensive to rebuild and cheap to keep around, which is why the host's persistence model matters more here than it does for a stateless API.
What the host needs to provide
Persistent state for an agent means a few specific things from the host.
A volume that survives the container's lifecycle. The mount comes back after a restart, a redeploy, or a scale event, and it comes back with the same data on the same path.
A volume attached to a specific service. A general blob store forces the agent to serialize and deserialize on every read and write, which works for infrequent snapshots but not for state that gets touched on every request. Local filesystem access is what most agent code assumes.
Datastores you can stand up next to the agent. Postgres for structured history, Redis for a fast cache, sometimes a dedicated vector store. Reaching those on a different platform over the public internet adds a network hop to every read and widens the attack surface.
Private networking between the agent and its data. The database, cache, and vector store are reachable by hostname on a private network, and only inside the environment.
Environment definitions that clone identically. Testing an agent change against a copy of production only works if the copy matches, which means the volume shape, the network topology, and the environment variables all come across together.
How Fly.io approaches it
Fly.io runs each workload as a lightweight VM, which they call a Fly Machine. Fly Volumes attach to those VMs for persistence. A volume lives on local NVMe on a specific host in a specific region, and it binds to the VM that mounts it. Your process reads and writes local files, and those files survive VM restarts.
The constraint is that volumes are pinned to a region and a specific VM. Fly's docs are explicit that "a Machine can only mount one volume at a time and a volume can be attached to only one Machine." Redundancy across regions is a separate pattern, usually an external Postgres cluster (Fly's own Managed Postgres, or a provider like Neon or Supabase) or LiteFS for replicated SQLite. LiteFS is worth caveating: Fly's docs note it is pre-1.0 and that "we are not able to provide support or guidance for this product," and there's an explicit warning against combining it with autostop/autostart. For a single-region agent that keeps its state in local files or SQLite, the Fly Volume model is a good fit. For multi-region, add a replicated filesystem or a managed database.
Private networking runs over 6PN, an IPv6 mesh that connects apps inside the same organization. Services address each other by internal hostname, and traffic stays inside Fly's network.
How Railway approaches it
Railway attaches volumes at the service level. Each service that needs persistence gets a volume mounted at a path you choose, and the volume follows the service through restarts and redeploys. One-click templates for Postgres, Redis, MySQL, and MongoDB stand up with a volume already attached and wired into the project's private network.
The model is deliberately simple: Railway does not expose region pinning or a separate replicated-filesystem product. For a single-instance agent with a database or two next to it, Railway works with very little configuration. Each service is limited to a single volume, and redeploying a service with a volume attached has a short downtime window, which the docs call out directly. Workloads that need multi-region persistence or multiple volumes per service typically pair Railway with an external managed database.
Private networking is scoped to the project. Services reach each other at SERVICE_NAME.railway.internal over IPv6.
How Suga approaches it
Suga runs each service as an always-on container with a persistent volume attached at whatever path the container writes to. The volume survives redeploys, restarts, and scale changes, and the container comes back with the same data on the same path. There is no scale-to-zero, so the process keeps running between requests.
For datastores that sit next to the agent, Suga has container templates for Postgres, Redis, and MariaDB with volumes pre-attached. Any other database deploys as a container with a volume attached the same way. The vector store, database, and queue run inside the same environment as the agent, on a private network that defaults to deny. Cross-container references let the agent read a downstream service's variables directly, using the syntax {{postgres.POSTGRES_PASSWORD}} or a full connection string like postgres://appuser:{{postgres.POSTGRES_PASSWORD}}@{{postgres.SUGA_PRIVATE_HOSTNAME}}:5432/app. Rotating the database password updates every consumer in one change.
Every deploy captures a commit message and the full environment definition, so a botched migration rolls back to any previous state. Cloning an environment copies the services, images, resource allocations, volume definitions, and networking wiring into a new environment. Volumes start empty in the clone, so if you want the agent's persisted state (conversation history, embeddings, checkpoint files) to carry over, seed them from a backup after the clone.
Secrets are per-container and marked sensitive. Once set from the dashboard or the Suga MCP, the value is write-only. Model provider keys, database passwords, and tool credentials attach to the container that uses them.
Which one fits which workload
Fly.io fits an agent whose state lives in local files or SQLite, where the operator is comfortable reasoning about regions and per-VM volumes. If you already know Fly's primitives, the volume model is direct and fast, and Fly's Managed Postgres covers the sibling-database case for structured state.
Railway fits an agent that wants a short setup from local dev to a running service with a database and cache attached. For a single-instance, single-region workload, the configuration effort stays small.
Suga fits an agent that wants an always-on process with a volume per container, template databases as siblings, a default-deny private network, and an environment you can clone end-to-end. The persistence primitives are per-container rather than per-project or per-VM, which keeps the topology explicit as the agent grows into extra services.