---
title: "How to Deploy and Scale Docker Containers on Railway in 2026"
description: "Deploy a Docker container on Railway from a Dockerfile or a pre-built image, then learn how Railway builds and caches images, how to scale with replica limits and regions, what Docker workloads cost, and how to troubleshoot failed deployments."
date: 2026-09-30T14:36:04.400Z
authors: ["Will Raphaelson"]
category: "Guide"
url: https://blog.railway.com/p/deploy-scale-docker-containers-railway
---

# How to Deploy and Scale Docker Containers on Railway in 2026

Docker made many parts of shipping software much easier. Package an application and its dependencies into a container image, and the same artifact can run locally, in CI, or in production.

But Docker didn't eliminate the infrastructure around the application. You still need somewhere to build the image, run and expose it to the network, store persistent data, manage environment variables, scale it, and understand what all of that costs. Railway handles all the infrastructure around the container.

This guide covers how to deploy Docker containers on Railway, how Railway builds and caches images, how to scale them, how Docker workloads are billed, and what to check when something goes wrong.

Railway can deploy a Docker container directly from a Dockerfile or run a pre-built image from a container registry. Once deployed, Railway handles networking, resource allocation, scaling, private service-to-service communication, and usage-based billing around the container. For most Docker applications, the main decision is whether you want Railway to build the image or whether you already produce the image somewhere else.

## How do you deploy a Docker container on Railway?

There are two main ways to deploy Docker containers on Railway: 1) have Railway build the image from a Dockerfile in your source repository, or 2) deploy an image that has already been built and pushed to a container registry. Which one you use mostly comes down to where you want the build to happen and how much control over that process you require.

### Option 1: Deploy from a Dockerfile

Connect a GitHub repository to a Railway service and Railway will look for a file named `Dockerfile` at the root of the source directory. If you use a different filename or keep the Dockerfile somewhere else, set `RAILWAY_DOCKERFILE_PATH` to tell Railway where to find it.

From there, Railway builds the image and deploys it as the service. You can configure environment variables and secrets in the Railway service, and if the application needs to receive traffic from the internet, generate a public domain for it.

Port issues are a common source of failed deployments. Railway provides the `PORT` environment variable, so the application should use that value rather than assuming the port it uses locally will also be the production port. For a public web service, bind to `0.0.0.0` rather than `localhost`. If the service also needs to accept connections over Railway's private network, use an IPv6-capable bind such as `::` where the framework supports it.

For example, an Express application might use:

```javascript
const port = process.env.PORT || 3000;

app.listen(port, "0.0.0.0");
```

If the Dockerfile uses an `ENTRYPOINT` or `CMD`, Railway uses that as the default start command for the service.

![Railway service Settings tab showing the Build section with the Dockerfile builder automatically detected, plus Dockerfile Path and Watch Paths fields](https://cms.railway.com/media/docker-guide-01-dockerfile-build-settings.png)

Railway uses BuildKit for Docker builds and supports cache mounts, so the way you structure the Dockerfile can affect how much work has to happen on subsequent deployments.

### Option 2: Deploy a pre-built Docker image

If you already have a CI pipeline that builds and tests Docker images, you can push the finished image to a container registry and have Railway deploy it directly. Railway supports registries that use standard Docker authentication, including Docker Hub, GitHub Container Registry, GitLab Container Registry, Quay, AWS ECR Public, Google Artifact Registry, and Microsoft Container Registry. [Private registry](https://docs.railway.com/builds/private-registries) credentials are available on the Pro plan.

The deployment itself is simple: build and test the image in CI, push it to the registry, then give Railway the image path and tag. Railway pulls the image and runs it without building it again. What you push to the registry is what Railway runs.

That setup makes sense when the container image is already the deployment artifact for your team. CI can own the build and testing process, while Railway handles running the image. It also avoids having two separate build processes that could produce slightly different artifacts.

<video src="https://cms.railway.com/media/docker-guide-add-registry-credentials.mp4" controls></video>

### Dockerfile vs. pre-built image

| | **Dockerfile** | **Pre-built image** |
|---|---|---|
| Where the image is built | Railway | Your CI pipeline or another build system |
| Input | Source code + Dockerfile | Container image |
| Build configuration | Dockerfile | External build pipeline |
| Useful when | You want Railway to handle builds | You already have an image build process |
| Deployment artifact | Built by Railway | Supplied by you |

## How fast are Docker builds and deployments on Railway?

There isn't one deployment time that applies to every Docker application. A small image with warm dependency caches is going to behave very differently from a large image that has to install dependencies and compile native packages from scratch.

However, [one Railway test](https://blog.railway.com/p/comparing-deployment-methods-in-railway) gives a useful reference point. The test used a small FastAPI application and measured both Dockerfile and pre-built image deployments. The Dockerfile deployment took 11 seconds to build and another four seconds to deploy, for 15 seconds total. Deploying the same application as a pre-built image skipped the build and took 6 seconds total. Those numbers describe that particular application and test environment rather than a guaranteed deployment time.

Railway's build infrastructure has changed since that test. Builder v3 runs builds inside microVMs on a static pool of hosts, with BuildKit caches kept warm across runs. Railway reported reaching 66,000 builds per hour at peak in May 2026, up from roughly 60,000 the previous week, and later reported another reduction in average build time of about 10 seconds through an OCI [metadata optimization](https://blog.railway.com/p/new-builder-scale-big).

For your application, the biggest factors are still dependent on your specifics: how much has to be downloaded, whether dependency layers can be reused, how large the build context is, and whether the application has an expensive compilation step.

![Railway build logs for a Dockerfile deployment showing the snapshot fetch, loading the build definition from backend/Dockerfile, and scheduling the build on a Metal builder](https://cms.railway.com/media/docker-guide-02-build-logs.png)

## How does Docker build caching work on Railway?

Railway uses BuildKit for Docker builds, so normal Docker layer caching rules apply. The order of the instructions in your Dockerfile matters because a change to an earlier layer can invalidate the layers that follow it. Railway also supports BuildKit [cache mounts](https://docs.railway.com/builds/dockerfiles#cache-mounts) for things like package-manager caches.

For example, in a Node application, you generally want to copy the dependency manifests and install dependencies before copying the rest of the source:

```docker
FROM node:22

WORKDIR /app

COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci

COPY . .
RUN npm run build

CMD ["npm", "start"]
```

With this structure, changing an application source file doesn't necessarily invalidate the dependency installation layer. If you copy the entire repository before running `npm ci`, a source-code change can force the dependency installation to run again.

The same idea applies outside Node: put files that change less often toward the beginning of the Dockerfile and files that change more often toward the end. A `.dockerignore` file keeps unnecessary files out of the build context, while a multi-stage build can keep compilers and other build-time dependencies out of the final image.

For builds that spend a lot of time downloading packages, BuildKit cache mounts can preserve package-manager caches between builds. Railway's [Dockerfile documentation](https://docs.railway.com/builds/dockerfiles) includes examples for configuring these mounts.

### Example Dockerfile optimized for caching

A multi-stage application might look something like this:

```docker
FROM node:22 AS build

WORKDIR /app

COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci

COPY . .
RUN npm run build

FROM node:22-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --omit=dev

COPY --from=build /app/dist ./dist

CMD ["node", "dist/index.js"]
```

The exact structure will depend on the application, but the basic idea is to avoid making every source-code change invalidate your dependency layers and to leave build-time dependencies out of the runtime image when you don't need them.

Cache hits aren't guaranteed. Changing an earlier layer, changing dependencies, changing the base image, or losing the relevant cache can result in a more expensive rebuild. If a build is particularly expensive and your CI system already produces a tested image, deploying that pre-built image is another option.

## Can Railway run any Docker image?

Railway can run most applications that work as a normal container, but when containerizing an application, keep in mind that Docker doesn't magically make that workload portable. You're deploying a container rather than a virtual machine where you control the underlying host.

| **Container requirement** | **Railway** |
|---|---|
| Normal web process | Supported |
| Environment variables | Supported |
| Persistent data | Use a Volume or external storage |
| Multiple related containers | Use multiple Railway services |
| Docker Compose application | Supported with some differences |
| Host-level or privileged behavior | May require changes |

### What networking configuration does a Docker container need?

For a public web application, the application should listen on `0.0.0.0` and use Railway's `PORT` environment variable. If the service needs to receive traffic over Railway's private network as well, make sure the server also supports IPv6 binding. An application listening only on `localhost` can appear healthy from inside the container while still being unreachable through the Railway network.

Railway handles the public networking layer when you generate a domain for the service. Services within a Railway project can also communicate over Railway's private network, which is what you would use for an application connecting to its database or another internal service.

### Where do you store persistent data for a Docker container on Railway?

Don't use the container filesystem as your database or long-term file store. Files written into the container should not be treated as durable application data across deployments.

For data that needs to persist, use a Railway Volume, database, or object storage, depending on the workload. A Volume is appropriate when the application needs persistent filesystem storage, but it comes with an important scaling constraint: Railway services with Volumes cannot use replicas. If the application needs to scale horizontally, keep shared state in a database, object store, or another external storage system instead.

## How do you scale a Docker container on Railway?

You can scale a Docker service vertically by giving it more CPU and memory capacity, or horizontally by running more replicas. Which approach makes sense depends on what's actually limiting the application. Railway supports configurable resource limits and horizontal replicas, and its current scaling model also includes [vertical autoscaling](https://docs.railway.com/deployments/scaling) up to the limits available to the service.

### How do you scale a Docker container vertically?

Start with the service's metrics rather than simply increasing the resource limit because traffic has increased.

Railway's Metrics tab shows CPU, memory, disk usage, and inbound and outbound network traffic, which gives you a way to see whether the application is actually running into a resource constraint. An application might need a relatively large memory footprint while using very little CPU, for example, while a worker could spend most of its time doing CPU-intensive work.

After examining metrics, adjust the limits if the application needs more room. Railway also provides [replica limits](https://docs.railway.com/pricing/cost-control#replica-limits) if you want to cap the resources available to an individual replica and prevent unexpected spikes in usage.

![Railway service Scale settings showing Regions & Replicas and Replica Limits sliders set to 2 vCPU and 12 GB memory](https://cms.railway.com/media/docker-guide-03-replica-limits.png)

### How do you scale a Docker container horizontally?

If one instance isn't enough, increase the number of [replicas](https://docs.railway.com/deployments/scaling#horizontal-scaling-with-replicas) for the service. Railway creates multiple instances of the service deployment, and replicas can also be distributed across regions.

Horizontal scaling requires a stateless service. Railway does not allow replicas on services with attached Volumes, so persistent state needs to live outside the service itself. In practice, that usually means putting sessions in a shared store, user uploads in object storage, and application state in a database.

![Railway Scale settings with the US West region set to 10 replicas](https://cms.railway.com/media/docker-guide-04-replicas.png)

### How do you deploy a Dockerized application across multiple regions?

Railway replicas can be deployed in multiple regions, so the same Dockerized service can run closer to users in different parts of the world. This is accomplished from the services setting pane. The application still needs to be stateless for horizontal scaling: shared sessions, uploads, and other persistent state should live outside the individual containers. Multi-region compute also doesn't automatically make the rest of the application multi-region, so consider where databases and other stateful dependencies run when deciding whether additional regions will actually reduce latency.

![Railway Scale settings with replicas in US West and Southeast Asia, and the Add Region dropdown listing EU West, US East, and Southeast Asia](https://cms.railway.com/media/docker-guide-05-multi-region.png)

## How much does it cost to run a Docker container on Railway?

There isn't a separate price for Docker hosting on Railway. The service is billed according to the resources it consumes, including CPU, memory, network egress, and persistent Volume storage.

The current resource rates are:

| **Resource** | **Price** |
|---|---|
| RAM | $10 / GB / month |
| CPU | $20 / vCPU / month |
| Network egress | $0.05 / GB |
| Volume storage | $0.15 / GB / month |

Railway also charges a base subscription fee, which goes toward resource usage. The current [Hobby plan](https://docs.railway.com/pricing/plans) is $5 per month and the Pro plan is $20 per month.

### How much does a small always-on Docker application cost?

Consider a small API that runs continuously and averages 512 MB of RAM, 0.05 vCPU, and 10 GB of monthly egress.

| **Resource** | **Average usage** | **Monthly rate** | **Estimated monthly cost** |
|---|---|---|---|
| RAM | 0.5 GB | $10 / GB | $5.00 |
| CPU | 0.05 vCPU | $20 / vCPU | $1.00 |
| Network egress | 10 GB | $0.05 / GB | $0.50 |
| **Total** | | | **$6.50** |

On the [Hobby plan](https://docs.railway.com/pricing/plans), the $5 monthly subscription goes toward that usage, so the additional resource charge would be approximately $1.50 for this example.

The difference from a fixed instance is that the container isn't tied to a particular CPU allocation. If the application averages 0.05 vCPU, you're paying for that usage rather than reserving a larger CPU allocation and paying for the unused capacity.

There is a corresponding tradeoff for applications with a large baseline memory footprint. A service that continuously uses 2 GB of RAM will continue to incur that memory cost even if CPU usage is low, and adding replicas increases the resources consumed by the application.

For workloads with long idle periods, Railway's [Serverless](https://docs.railway.com/deployments/serverless) mode can stop an inactive service and reduce compute costs while it's asleep. Railway also provides [usage limits](https://docs.railway.com/pricing/cost-control#configuring-usage-limits) and resource limits if you want to put a ceiling on how much a workload can consume.

## How do you deploy a multi-container Docker application on Railway?

A Dockerized application doesn't have to mean one container. If the application consists of several containers, create a Railway service for each one. Railway's [Docker Compose support](https://docs.railway.com/builds/dockerfiles#docker-compose) can also help translate an existing Compose application into a Railway project, although Railway does not run the Compose file as the production orchestrator.

For a [Compose application](https://docs.railway.com/guides/docker-compose), a service using `build:` maps to a Railway service that builds from its Dockerfile, while a service using `image:` can be deployed from the existing image. `volumes:` maps to Railway Volumes, and Railway provides private networking between services automatically.

## How do you run a background worker in Docker on Railway?

A background worker on Railway is just a service running its own container. Deploy it from a Dockerfile or pre-built image the same way you would a web service, but don't generate a public domain for it. Instead of listening for incoming HTTP traffic, the container runs the worker process defined by its start command.

In a typical application, the web service and worker can use the same image with different start commands, or separate Dockerfiles if their runtime requirements differ. They can communicate with the same database, queue, or other internal services over Railway's private network. Scale the worker independently from the web service based on its own CPU, memory, and queue-processing requirements.

## Should you use a Dockerfile, Railpack, or a pre-built image?

Railway doesn't require a Dockerfile for every application. [Railpack](https://docs.railway.com/builds/railpack) can detect an application's stack, determine how to install dependencies and run the build, and package the result into a container image. Railway currently lists Node, Python, Go, PHP, HTML/Staticfile, Java, Ruby, Deno, Rust, Elixir, and Shell scripts as supported out of the box. Railway also publishes deployment guides for common frameworks such as Next.js, Express, FastAPI, Django, Rails, and Spring Boot.

Railpack isn't limited to whatever defaults Railway happens to detect, either. Build configuration can be customized through environment variables or a Railpack configuration file, including language versions, build and start commands, packages, and cache directories.

### When should you use Railpack?

Use Railpack when the application fits the build and runtime configuration Railway can detect automatically and you don't need to customize the underlying image. It's one less file to maintain and one less layer of build configuration to think about.

For example, a [Node](https://railpack.com/languages/node) application can be detected from its `package.json`, while [Python](https://railpack.com/languages/python) applications can be detected from files such as `requirements.txt` or `pyproject.toml`. Railpack's Java provider detects Gradle projects from a `gradlew` wrapper and Maven projects from a `pom.*` file.

### When should you use a Dockerfile?

A Dockerfile makes the most sense when the details of the image itself matter. That might mean installing system packages, using a particular base image, compiling native dependencies, controlling exactly which files make it into the runtime image, or using a multi-stage build. It also makes the build environment explicit in the repository, which can be useful when the application has requirements that don't fit the default build process.

### When should you deploy a pre-built image?

If your CI system already builds the application, you might as well use that image in production. That way, the artifact tested by CI can be the artifact deployed to Railway. Railway doesn't rebuild it or create a second version of the application; it pulls the image from the registry and runs it. That can be useful for teams that want their container registry and CI pipeline to remain the source of truth for application artifacts.

| **Deployment method** | **Best suited for** |
|---|---|
| Railpack | Conventional applications that fit Railway's automatic detection |
| Dockerfile | Applications that need explicit control over the image |
| Pre-built image | Applications where CI already produces the image |

## How do you troubleshoot a Docker deployment on Railway?

In general, it's wise to start with the logs before messing with the Dockerfile. Railway exposes build and deployment logs in the dashboard, and you can also use the CLI to inspect them.

From the CLI:

```bash
railway logs
```

shows deployment logs, while:

```bash
railway logs --build
```

shows the build output. You can also fetch a fixed number of recent lines with:

```bash
railway logs --lines 100
```

![Railway deployment details for a failed deployment, with Initialization, Build, and Deploy passing and the Network healthcheck step failing](https://cms.railway.com/media/docker-guide-06-healthcheck-failure.png)

### Why does my Docker container build but immediately exit?

If the container exits with code 0, the process completed successfully; it just didn't keep running. For a web service or worker that is supposed to stay alive, check the command that starts the container first. For a Docker deployment, Railway uses the Dockerfile's `ENTRYPOINT` and `CMD` as the default start command.

If the command is wrong, the application crashes during startup, or the command simply finishes instead of starting a long-running process, the container will exit. The deployment logs should show which process ran and what it returned. It's also worthwhile to check environment variables. A missing production variable can cause an application that works locally to fail immediately after it starts.

### Why is my Docker container running but I can't reach it?

Check the host and port first. For public networking, the application should listen on `0.0.0.0` and use Railway's `PORT` environment variable. If the service needs to receive traffic over Railway's private network as well, make sure the server also supports IPv6 binding.

If the application is listening correctly, check that the service has a public domain if you're trying to reach it from the internet.

![Railway service Networking settings showing a generated public domain with Generate Domain, Custom Domain, and TCP Proxy buttons](https://cms.railway.com/media/docker-guide-07-public-networking.png)

### Why are my Docker builds slow?

Open the build logs and find the step that's actually taking up the most time. A large build context, missing `.dockerignore`, heavy dependency installation, native compilation, or an invalidated cache can all make a build take much longer than expected.

If dependencies are being installed on every source-code change, look at the order of your `COPY` instructions. Dependency manifests should generally be copied and installed before the application source code so that changes to source files don't invalidate the dependency layer.

If the image itself is large, look at the base image and whether a multi-stage build can remove build-time dependencies from the final image.

### Why do files disappear after a Docker redeploy?

If the application wrote the files into the container filesystem, they were never durable application storage to begin with. Move persistent data to a Railway Volume, database, or object storage.

If you're using a Volume, keep the storage architecture separate from the application's horizontal scaling strategy. Railway does not support replicas on a service with an attached Volume. If the application needs multiple replicas, move shared state to a database, object store, or other external storage system.

### Why does my Docker image work locally but fail on Railway?

Start by comparing the production environment with your local environment. Check the environment variables available to the container, file permissions, CPU architecture, and any dependencies on `localhost` or files that exist on your machine but were never copied into the image.

Architecture mismatches are particularly easy to run into when building images on Apple Silicon. Railway's private registry guide specifically calls out building with `--platform linux/amd64` when necessary, since Docker on Apple Silicon defaults to `arm64`.

A multi-container application can also fail because a dependency is still pointed at `localhost`. Inside a container, `localhost` means that container, so a Postgres connection needs to use the database service's private network address rather than the application's own localhost interface.

If the application starts but requests are failing, check the HTTP logs as well as the application logs. Request status and timing information can help distinguish an application error from a networking or upstream latency problem.

## Conclusion

Docker gives you a fantastic portable application artifact, but not somewhere to run, observe, and manage it. Railway can build that artifact from a Dockerfile, deploy an image produced by your own CI pipeline, run several containers as separate services, attach persistent storage where you need it, and scale services across replicas.

The choice of deployment method mostly comes down to how much control you need over the image and where you want the build to happen. If the application fits Railpack, you may not need a Dockerfile at all. If you need control over the runtime environment, using one can provide finer-grained control. If your CI system already produces a tested image, deploy that image directly.

Once the container is running, the same infrastructure considerations apply as with any other application: use Railway's supplied port, keep durable state out of the container filesystem, use private networking between internal services, and look at actual resource usage before deciding how to scale.

---

Open this post in a browser: https://blog.railway.com/p/deploy-scale-docker-containers-railway
