Railway + Docker Deployment
Prerequisites
# Install Railway CLI (macOS)
brew install railway
# Login
railway login
# Link to existing project or create new
railway link # existing
railway init # new
Multi-stage Dockerfile
Optimized for Next.js with custom server:
# ── Stage 1: Install dependencies ──
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# ── Stage 2: Build ──
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# ── Stage 3: Run ──
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./
COPY --from=builder /app/server.mjs ./
COPY --from=builder /app/next.config.ts ./
USER nextjs
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.mjs"]
If not using a custom server, replace the last line with CMD ["npm", "start"].
.railwayignore
Prevents uploading unnecessary files (reduces deploy time):
node_modules/
.next/
.env*.local
*.tsbuildinfo
.git/
.dockerignore
node_modules
.next
.git
*.md
.env*.local
Deploy
# Deploy and detach (non-blocking)
railway up --detach
# Watch logs
railway logs -f
Environment variables
Set via Railway dashboard or CLI:
railway variables set NODE_ENV=production
railway variables set ADMIN_PWD=your-secret
railway variables set PORT=3000
Railway auto-injects PORT — your server must listen on process.env.PORT.
Troubleshooting
Upload timeout
If railway up times out during upload:
- Check
.railwayignore— make surenode_modules/,.next/,.git/are excluded - Check large files — videos/assets in
public/add upload time - Local proxy interference — if you use a proxy (e.g. Clash, Surge), it can intercept Railway CLI uploads. Bypass with:
HTTP_PROXY="" HTTPS_PROXY="" ALL_PROXY="" \
http_proxy="" https_proxy="" all_proxy="" no_proxy="*" \
railway up --detach
Build failures
- Check
railway logsfor the specific error - Ensure
Dockerfilecopies all files needed at runtime - Verify
ENV PORTmatches what your server listens on
Video/large assets
For large static files (videos, images):
- Option A: Serve from
public/— simple but increases Docker image and deploy time - Option B: Use external storage (S3, R2, CDN) — better for production
- Compress videos before adding to
public/:
ffmpeg -i input.mp4 -c:v libx264 -crf 26 -preset slow \
-c:a aac -b:a 128k -movflags +faststart output.mp4
Railway-specific notes
- Railway provides HTTPS automatically (no cert setup needed)
- Custom domains:
railway domainto get a*.up.railway.appURL, or add custom domain in dashboard - Railway uses Nixpacks by default; having a
Dockerfileoverrides this - Healthcheck: Railway pings your app — ensure it responds to HTTP on
PORT
Checklist
-
Dockerfilewith multi-stage build (deps → builder → runner) -
.railwayignoreexcludesnode_modules/,.next/,.git/ -
.dockerignoreexcludes dev-only files - Server listens on
process.env.PORT - Environment variables set via Railway dashboard/CLI
- Large assets compressed or moved to external storage
- Tested locally with
docker build -t app . && docker run -p 3000:3000 app