Railway

Deploy Drupal CMS

Pre-configured Drupal CMS + PostgreSQL, installs itself on deploy.

Deploy Drupal CMS

/opt/drupal/web/sites/default

Just deployed

/var/lib/postgresql

Drupal on Railway

Production template to deploy Drupal 11 on Railway with one click: automatic installation, private PostgreSQL database, persistent files and built-in healthcheck. No web installer and no manual configuration — deploy and log in with your admin user.

> English quick summary: One-click Drupal 11 on Railway — a pinned drupal:11.4.5 Apache image + PostgreSQL 17.6. Auto-installs on first boot with generated admin credentials, persists uploads on a volume at sites/default, exposes /healthz.php for healthchecks, wires trusted hosts + reverse proxy. Networking: Apache listens on IPv4 and the service sets PORT=80 so Railway routes correctly (avoids the classic 502). Docs below are now in English.


Template overview (marketplace listing)

The long-form overview for the Railway marketplace listing. It keeps each required section, with none of the scaffold text.

Deploy and Host Drupal with Railway

Drupal is a leading open-source content management system powering millions of websites, from personal blogs to enterprise platforms. This template deploys Drupal 11 with a private PostgreSQL 17 database on Railway in one click — fully installed, configured and ready to build your site on, with no setup wizard and nothing hardcoded.

About Hosting Drupal

Hosting Drupal involves running its PHP application behind a web server, connecting it to a database, and persisting uploaded files across deployments. On Railway this template handles all of it automatically: the first boot waits for PostgreSQL, installs Drupal via Drush using generated admin credentials, mounts a volume for all uploads, and exposes a readiness endpoint (/healthz.php) so traffic only arrives once your site is truly usable. Updates are applied automatically on each redeploy.

Why Deploy Drupal on Railway?

  • Zero-config, one click: no install wizard, no manual variable setup — the entrypoint installs, configures and hardens the site on first boot using template secrets.
  • Persistent by default: uploads and the database live on Railway Volumes, so your content survives every redeploy.
  • Private by design: PostgreSQL stays on Railway's private network — the database is never exposed to the public internet.
  • Production-ready: pinned versions (Drupal 11.4.5, PHP 8.4, PostgreSQL 17.6, Drush 13), trusted hosts, reverse-proxy wiring and a real healthcheck gating traffic.
  • Free-tier friendly: sized to fit within Railway's hobby/free credits for small sites.

Common Use Cases

  • Corporate websites and marketing sites with structured content
  • Blogs, magazines and news portals with editorial workflows
  • Intranets and member portals with role-based access control
  • E-commerce storefronts built on Drupal Commerce
  • Government, education and NGO sites requiring accessibility and multilingual content

Dependencies for Drupal Hosting

  • Apache + PHP 8.4 — serves Drupal from the official pinned image (drupal:11.4.5)
  • PostgreSQL 17 — primary data store, private-networked, never exposed publicly
  • Drush 13 — Drupal's CLI, used by the boot script to install and update the site
  • Railway Volumes — persistent storage for uploads and database files
  • Railway Healthchecks — readiness gating via /healthz.php
Deployment Dependencies

What's included

ServiceImage / sourcePinned versionVolumes
DrupalThis repo (Dockerfile, based on the official image)drupal:11.4.5-php8.4-apache-bookworm, Drush 13.7.6/opt/drupal/web/sites/default
PostgresOfficial imagepostgres:17.6-alpine/var/lib/postgresql

The two services talk over Railway's private network (postgres.railway.internal); the database is never exposed to the internet.

What happens on deploy

  1. Railway starts Postgres and the Drupal container.
  2. The entrypoint waits for Postgres to accept connections and generates sites/default/settings.php reading the credentials from environment variables (nothing hardcoded).
  3. If the database is empty, it runs the full installation with Drush (~1 min): standard profile, admin user and generated password.
  4. The /healthz.php healthcheck turns green only when the site is fully working, and Railway routes traffic.
  5. On redeploys it runs drush updatedb + cache rebuild: uploads and content survive on the volume.

Networking on Railway (important)

The official image exposes Apache on port 80, but Railway routes traffic by the PORT variable (default 8080), so you must set PORT=80 or the proxy returns 502 connection refused. In addition, Debian's ports.conf listens on [::]:80 (IPv6-only in some environments) while Railway connects over IPv4; the Dockerfile copies docker/ports.conf with Listen 0.0.0.0:80 to avoid it. If you get a 502, it's almost certainly one of these two causes; see docs/TROUBLESHOOTING.md.

After deployment

  1. Wait for the Drupal service to turn green (the first boot installs the site, ~2 min).
  2. Open your domain .up.railway.app.
  3. Log in with user admin and the password from the DRUPAL_ADMIN_PASSWORD variable (Drupal service → Variables tab).
  4. Change the password from your profile if you like and set the site email.
  5. (Optional) Schedule external cron (e.g. cron-job.org) pointing to the /cron/... URL shown in the deploy logs.

Variables

They are all preconfigured in the template — you don't need to create any. The Drupal service consumes the database over Railway's private network, referencing the Postgres service, so if you rename the Postgres service you only need to update the references.

Variable (Drupal service)Value / referenceDescription
PORT80Port Apache listens on, the one Railway uses to route. Without it you get error 502.
PGHOST${{Postgres.RAILWAY_PRIVATE_DOMAIN}}Private .railway.internal host of Postgres
PGPORT5432Postgres port
PGUSERpostgresPostgres superuser
PGPASSWORD${{Postgres.POSTGRES_PASSWORD}}Postgres password (generated)
PGDATABASEpostgresDefault database of the image
DRUPAL_HASH_SALTgenerated (secret)Drupal hash salt
DRUPAL_CRON_KEYgenerated (secret)Cron URL key
DRUPAL_ADMIN_USERadminInitial admin user
DRUPAL_ADMIN_PASSWORDgenerated (secret)Admin password — see it in Variables
DRUPAL_ADMIN_MAILadmin@example.comAdmin email (change it)
DRUPAL_SITE_NAMEMy Drupal SiteSite name
DRUPAL_SITE_MAILadmin@example.comSite sender email
TRUSTED_HOSTS(empty)Optional: extra trusted host patterns, comma-separated
DRUPAL_ERROR_LEVELhideSet verbose to debug errors on screen

> Note on references: this template uses the official Postgres image as an image-based service. Unlike Railway's "managed" Postgres, that image does not expose PGHOST/PGPORT/PGUSER/PGDATABASE automatically; that's why RAILWAY_PRIVATE_DOMAIN and POSTGRES_PASSWORD are referenced and fixed values are assigned in the template itself. If you prefer managed Postgres, swap the service for Railway's and adjust the references to ${{Postgres.PGHOST}} etc.

Credentials

  • Drupal admin: user DRUPAL_ADMIN_USER (default admin) and password DRUPAL_ADMIN_PASSWORD (generated per deploy; see Drupal service → Variables).
  • Database: private network only (never exposed to the internet); the entrypoint resolves it from the PG* variables.
  • Each deploy generates unique secrets (DRUPAL_ADMIN_PASSWORD, DRUPAL_HASH_SALT, DRUPAL_CRON_KEY, POSTGRES_PASSWORD) with ${{secret(...)}} — nothing hardcoded.

Estimated cost

With default sizes (512 MB RAM on Drupal, 1 GB volume): approximately 5–8 USD/month, within the Hobby credit with a narrow margin. Tips to reduce it in docs/COSTOS.md.

Local development

cp .env.example .env      # edit the password if you want
docker compose up --build
# open http://localhost:8080

Repository structure

├── Dockerfile                  # final image (official drupal + drush + tuning)
├── railway.json                # Dockerfile builder + healthcheck
├── docker-compose.yml          # equivalent local stack
├── docker/                     # entrypoint, settings, healthz, ports, php, remoteip
└── docs/                       # ARQUITECTURA, COSTOS, TROUBLESHOOTING, PUBLICAR

Troubleshooting

See docs/TROUBLESHOOTING.md. Most common:

  • The first deploy takes a while: the installation runs during the healthcheck (300 s timeout).
  • "The provided host name is not valid": add your domain to TRUSTED_HOSTS.
  • 502 Bad Gateway: almost always the routing; check PORT=80 and the IPv4 bind (see Networking).

License

GPL-2.0-or-later, like Drupal. This repo only adds glue (entrypoint, settings, healthcheck) on top of the official Drupal image, whose brand and logo belong to the Drupal Association.


Template Content

More templates in this category

View Template
Libredesk - Complete Setup
Complete self-hosted omnichannel customer support desk.

codestorm
1
View Template
Paperless-ngx
Paperless-ngx — document management with OCR and full-text search

INAPP
1
View Template
Instatic CMS - Postgres
Design, build and manage powerful static sites from state-of-the-art CMS

Instatic
150