Deploy Node.js on Railway with Dockerfile and Environment Variables
Deploy a Node.js service on Railway by linking a GitHub repo with a Dockerfile. Build runs automatically, PORT is injected, and a public URL is provisioned.
20 Aug 2025, 06:51 UTC

Railway lets you ship a Node.js service instantly by linking a GitHub repo that contains a Dockerfile. On push, Railway runs docker build on the repo root, injects the environment variables you set in the dashboard, starts the container, and gives you a public URL. The service is reachable only if your app listens on the PORT Railway provides.
How Railway builds from a Dockerfile
When you connect a GitHub repository to a Railway project, the platform checks for a Dockerfile in the repo root. If present, Railway skips its default builder and runs docker build with that file. Environment variables defined in the service Variables tab are injected at runtime, not build time, unless you reference them in the Dockerfile.
Railway assigns a dynamic PORT to the container. Your process must bind to process.env.PORT. Railway performs a health check against that port. If the process listens on a hard-coded port, the deployment is marked unhealthy and the URL stays unreachable.
Worked configuration
Repo layout: package.json index.js Dockerfile .gitignore
index.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Service is running in mode: ' + (process.env.NODE_ENV || 'development'));
});
app.listen(port, () => {
console.log('Server is listening on port ' + port);
});
The app reads PORT from the environment and falls back to 3000 for local runs. It also echoes NODE_ENV so you can verify variable propagation.
Dockerfile
FROM node:20-slim
WORKDIR /usr/src/app
COPY package*.json .
RUN npm install --production
COPY . .
EXPOSE 3000
CMD node index.js
Copy dependency manifests first to reuse Docker layer cache. EXPOSE documents the intended port; Railway does not require it to match, but it clarifies intent. The final CMD runs the app as the container entrypoint.
package.json
{
"name": "railway-node-demo",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"express": "^4.18.2"
}
}
Deploy and verify
- Commit the files to GitHub. Ensure .gitignore contains node_modules/ to avoid large commits.
- In Railway, create a new project and select Deploy from GitHub. Choose the repository and branch. Railway detects Dockerfile and starts a build.
- In the service Variables tab, add NODE_ENV=production. Variable names are case-sensitive.
- Open the Deployments tab. The build log should show docker build steps and a container start message. The log from the app should include Server is listening on port [assigned number].
To confirm env var propagation, open the public URL shown in the dashboard. Change NODE_ENV to staging in Variables, trigger a redeploy, and reload the URL. The response text should reflect the new value.
Limits and common mistakes
Free tier limits include 500 build minutes per month, 1 GB RAM per service, and a shared outbound bandwidth pool. Exceeding build minutes throttles new builds. Memory pressure causes OOM kills.
- Hard-coded port. Using app.listen(3000) without process.env.PORT causes health check failures because Railway routes to the assigned PORT.
- EXPOSE mismatch and missing bind. EXPOSE is documentation only. The process must actually listen on the assigned PORT.
- Committed node_modules. This inflates repo size, slows git pushes and docker build context transfer, and can hit build size limits.
- Ephemeral filesystem. The container filesystem is wiped on redeploy. Write operations must go to an external database or managed volume.
- Case-sensitive env vars. A typo like node_env will be undefined in code with no dashboard warning.
Practical checks: watch the Deployments log for a successful docker build and a listening message on the assigned port. Test the public URL immediately after deploy. If unreachable, inspect logs for port binding errors and verify process.env.PORT usage.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.