Deploy Nginx

High-performance web server, reverse proxy and HTTP cache

Deploy Nginx

Just deployed

/data

Deploy and Host NGINX on Railway

NGINX is the web server behind roughly a third of the internet's busiest sites. It serves static files, forwards HTTP, load-balances across backends and caches what it proxies, from one small event-driven process that handles tens of thousands of concurrent connections on modest hardware. Teams reach for it when they need a front door: to publish a built site, put several backends behind one domain, or stand in front of an application never meant to face the internet.

This template deploys a single nginx service with a persistent volume, with the awkward parts of running a web server on a platform already handled. Deploy NGINX here and it listens on the port Railway assigns over IPv4 and IPv6, sizes its worker processes from the container's CPU quota rather than the host's, and recovers the true client address from X-Forwarded-For instead of logging Railway's rotating edge. The volume mounts at /data: your site lives in /data/www and the cache in /data/cache, so both survive every redeploy. The configuration is rendered from environment variables at boot and checked with nginx -t before the server starts.

Diagram of the single NGINX service and its volume on Railway

Getting Started with NGINX on Railway

Open the deployed URL as soon as the build finishes. There is no login and no setup wizard — NGINX serves a placeholder page describing the available settings, which is also how you confirm the deployment is healthy. It is seeded onto the volume only while the site root is empty, so it disappears the moment you publish your own index.html.

The fastest way to publish a real site is to set SITE_SOURCE_URL to a .tar.gz or .zip of your build output. It is fetched on every boot and unpacked only when its contents have changed, so redeploying publishes a new build. Alternatively, upload files onto the volume with railway volume files --volume upload ./index.html /www/index.html. For a single-page app, set SPA_MODE=true so unknown paths fall back to index.html.

To put NGINX in front of your other Railway services, set PROXY_ROUTES to a comma-separated list of /path=upstream pairs — for example /api=http://api.railway.internal:3000. Following proxy_pass's own convention, an upstream ending in / strips the matched prefix and one without it passes the path through. Check /healthz, which always answers 200 anonymously and is the path Railway's health check uses.

NGINX landing page listing static hosting and proxy settings

A custom static site served by NGINX from the Railway volume

Live NGINX checks showing proxy, cache and header results

About Hosting NGINX

NGINX solves two problems that appear in almost every deployment. The first is serving files quickly and correctly — right MIME types, compression, cache headers and a real 404. The second is routing: the internet reaches one hostname, and something must decide which backend answers which path. Self-hosting puts that logic in configuration you control.

Key features in this deployment:

  • Static file serving from a persistent volume, with optional SPA fallback and directory listings
  • Path-based reverse proxying to any HTTP upstream, including private Railway services
  • On-disk response caching for proxied traffic, with an X-Cache-Status header
  • gzip compression, plus gzip_static so a pre-built .gz sibling is served as-is
  • HTTP basic auth over the whole site, hashed at boot, and per-client-IP rate limiting
  • Security headers — nosniff, X-Frame-Options, referrer policy, COOP and HSTS — on every response

There is one service and one volume. NGINX needs no database, no queue and no worker tier.

Why Deploy NGINX on Railway

Railway removes the parts of running a web server that are not about serving pages:

  • TLS certificates issued and renewed at the edge, with HTTP redirected to HTTPS
  • A persistent volume for site content and cache, surviving every deploy
  • Private networking to your other services, so backends need no public domain
  • Health checks, restart policy and logs configured out of the box
  • Custom domains attached without touching a config file

Common Use Cases

  • Publishing a static site, docs build or SPA, with compression and cache headers already set
  • Putting one public domain in front of several private Railway services, split by URL path
  • Caching an expensive or rate-limited upstream API on disk to cut origin traffic
  • Fronting an app that has no auth of its own with basic auth and per-IP rate limiting

Dependencies for NGINX

  • nginx — the whole deployment. Built from nginx:alpine (mainline, currently 1.31.5) plus a boot script that renders the configuration. Source: gridalpha/nginx-railway.
  • Volume — mounted at /data. The site root and cache sit one level below the mount root, because every Railway volume ships a lost+found directory.

Environment Variables Reference

VariableDefaultPurpose
SITE_ROOT/data/wwwDirectory served as the site root
SITE_SOURCE_URL(unset)Archive unpacked over the site root at boot
SITE_SOURCE_STRIP0--strip-components; use 1 for a GitHub source tarball
SPA_MODEfalseFall back to /index.html for unknown paths
DIRECTORY_LISTINGfalseServe an index for directories with no index file
PROXY_ROUTES(unset)Comma-separated /path=upstream proxy routes
CACHE_ENABLEDfalseCache proxied responses on the volume
GZIP_ENABLEDtruegzip plus gzip_static
HSTS_MAX_AGE315360000 removes the HSTS header
BASIC_AUTH_USER(unset)Set with BASIC_AUTH_PASSWORD to require a login
RATE_LIMIT_RPS(unset)Requests per second per client IP

Deployment Dependencies

Hardware Requirements for Self-Hosting NGINX

NGINX is unusually cheap to run. The defaults below comfortably serve a static site or a small gateway.

ResourceMinimumRecommended
CPU0.1 vCPU1 vCPU
RAM64 MB512 MB
Storage1 GB volume5 GB volume, more if caching
Runtimenginx 1.31 (Alpine)nginx 1.31 (Alpine)

Raise storage first: the volume holds both your site and the cache, so keep CACHE_MAX_SIZE below the volume size.

Self-Hosting NGINX

The image runs anywhere Docker does. This clones the source repository and builds it:

git clone https://github.com/gridalpha/nginx-railway.git
cd nginx-railway
docker build -t nginx-railway .
docker run -p 8080:8080 -e PORT=8080 -v nginx-data:/data nginx-railway

The next block runs the same server as a gateway: a site from the volume, two paths proxied to backends, and the responses cached.

docker run -p 8080:8080 \
  -e PORT=8080 \
  -e SPA_MODE=true \
  -e CACHE_ENABLED=true \
  -e PROXY_ROUTES=/api=http://api:3000,/auth=http://auth:9000/ \
  -e BASIC_AUTH_USER=admin -e BASIC_AUTH_PASSWORD=change-me \
  -v nginx-data:/data nginx-railway

Is NGINX Free to Self-Host?

NGINX is open source under a permissive two-clause BSD licence, with no paid tier, seat count or feature gate in the version deployed here. F5 sells a commercial distribution, NGINX Plus, adding active health checks and support — neither of which this template needs. Self-hosting costs only the compute and storage the container uses.

FAQ

What is NGINX? NGINX is an open-source web server, reverse proxy, load balancer and HTTP cache. It is event-driven rather than thread-per-connection, which is why one small instance handles very high concurrency.

What does this Railway template deploy? One nginx service with a persistent volume at /data. It serves static files from /data/www, optionally proxies paths to other services, and optionally caches those responses in /data/cache. No database or supporting service is involved.

Why does the template include a volume? Container filesystems are discarded on every deploy. The volume keeps your site and cache separate from the image, so you can redeploy to pick up an NGINX security release without touching a page.

How do I put NGINX in front of my other Railway services? Set PROXY_ROUTES to /path=upstream pairs using each service's private hostname, such as /api=http://api.railway.internal:3000. Only NGINX needs a public domain. Upstreams are re-resolved as they change, so redeploying a backend does not strand a stale address.

Does self-hosted NGINX see the real visitor IP behind Railway's proxy? Yes. Railway's edge appends its own rotating address to X-Forwarded-For, so the usual real_ip_recursive recipe lands on a Railway address. This configuration reads the leftmost entry instead, and uses it for access logs, rate limiting and the headers sent upstream.

How do I password-protect the site? Set BASIC_AUTH_USER and BASIC_AUTH_PASSWORD. The htpasswd file is generated at boot, so no password is stored in the image, and /healthz stays anonymous so the health check keeps passing.


Template Content

More templates in this category

View Template
Rocky Linux
Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
47
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
51