Railway

Deploy Ghost

Substack alternative. Publishing, email newsletters and Stripe memberships

Deploy Ghost

Just deployed

/data

Ghost

ghost:6

Just deployed

/var/lib/ghost/content

MySQL

mysql:8

Just deployed

/var/lib/mysql

Ghost logo

Deploy and Host Ghost on Railway

Ghost is an open source publishing platform combining a writing-first CMS, a native email newsletter and paid memberships in one application. Writers, journalists and companies running an engineering blog use it as a self-hosted alternative to Substack, Medium and a plugin-laden WordPress install: you keep the content, the subscriber list and the payment relationship, with no revenue share on paid subscriptions.

Deploy Ghost on Railway and this template wires up the three pieces a production install needs. The Ghost container serves both the public site and the admin app on port 2368 behind Railway's TLS edge. A MySQL 8.4 service holds every post, member and setting, reached over the private network so the database has no public address. A volume at /var/lib/ghost/content stores uploaded images, media and themes, so files survive every redeploy, and Redis backs Ghost's asynchronous caches. Schema migrations run automatically on first boot, so the site is ready as soon as the deploy goes green.

Ghost Railway architecture

Getting Started with Ghost on Railway

Open the generated Railway URL and you land on the default Ghost front end with a "Coming soon" placeholder post. There are no default credentials: go to /ghost/ and Ghost redirects you to a one-time setup screen asking for a site title, your name, an email address and a password of at least 10 characters. Submitting it creates the site owner, signs you in and opens the Analytics dashboard.

Publish a real post next. Click Posts → New post, add a title and a paragraph, then use Add feature image to upload a picture — the best single check that the deployment is healthy, because it exercises the volume, the image resizer and the database at once. Publish, then open your site root: the post should appear in the latest-posts list, at its own slug, in /rss/ and in /sitemap-posts.xml. If the image renders publicly, the volume is mounted correctly.

Then configure email before inviting anyone. Ghost's default transport delivers mail straight to recipient servers, which does not work from Railway, and Ghost 6 emails staff a confirmation code when they sign in from a new device. Your first session lasts 180 days, so the site keeps working — but set the SMTP variables below, using Mailgun, Postmark or SES, before it expires, or you cannot sign in from a second browser.

Ghost publication homepage listing the latest published post Ghost post page with its uploaded feature image Ghost admin posts list showing a published article

About Hosting Ghost

Ghost is a Node.js application that owns both a database schema and a content directory. Self-host it when you want the subscriber list and payment relationship in your own infrastructure, when you need themes or routes a hosted plan restricts, or when you want to drive the site headlessly.

  • Writing-first editor with cards for images, galleries, embeds and code
  • Native newsletters to segmented member lists, with open/click analytics
  • Paid memberships through a direct Stripe connection, with no platform fee
  • Handlebars themes, custom routing via routes.yaml, theme upload from the admin
  • Content API and Admin API with key-based auth, plus webhooks

The Ghost service is the whole application — public site, admin, API and background jobs in one process. MySQL is the system of record for posts, members and settings. The volume holds everything that is a file rather than a row. Redis caches values Ghost reads asynchronously, such as remote image dimensions.

Why Deploy Ghost on Railway

Railway removes the server administration self-hosting Ghost normally implies:

  • TLS, HTTP/2 and a custom domain, with no nginx to configure
  • Private networking to MySQL, so the database stays unexposed
  • A volume that survives every redeploy
  • Vertical scaling by slider as your audience grows
  • Logs, metrics and one-click rollback

Common Use Cases

  • A company engineering blog or changelog, with invite-only staff accounts
  • A paid newsletter where Stripe subscriptions go straight to your account
  • A headless backend feeding a Next.js, Astro or Hugo front end via the Content API
  • A docs or community site needing custom URLs via routes.yaml

Dependencies for Ghost

  • Ghostdocker.io/library/ghost:6, the official Docker Library image. Serves site and admin on port 2368 and migrates the schema at boot.
  • MySQLdocker.io/library/mysql:8, resolving to the MySQL 8.4 LTS line. Ghost supports only MySQL 8 in production, so the major is pinned deliberately.
  • Redis — Railway's managed Redis, for the asynchronous cache adapters.

Environment Variables Reference

VariablePurpose
urlPublic base URL; canonical links and feed URLs derive from it
database__clientSet to mysql; Ghost maps this to the mysql2 driver
database__connection__*Host, port, user, password and database name
logging__transportsMust be ["stdout"] or logs go to a file in-container
PORTPort Railway routes public traffic to (2368)
mail__transport / mail__options__*SMTP for newsletters, invites and sign-in codes
adapters__cache__settings__adapterMust stay MemoryCache; settings are read synchronously
NODE_OPTIONSCaps the Node heap to the container, not the host

Ghost reads every configuration key from the environment, using a double underscore per level of nesting: database.connection.host becomes database__connection__host. Values parse as JSON where possible, which is why ["stdout"] arrives as a real array.

Deployment Dependencies

Hardware Requirements for Self-Hosting Ghost

ResourceMinimumRecommended
CPU1 vCPU2 vCPU
RAM1 GB Ghost, 1 GB MySQL2 GB Ghost, 2 GB+ MySQL
Storage1 GB volume5–20 GB by media volume
RuntimeNode.js 22, MySQL 8Node.js 22, MySQL 8.4 LTS

Self-Hosting Ghost with Docker

The quickest local run uses SQLite — fine for evaluation, unsupported for production:

docker run -d --name ghost -p 2368:2368 \
  -e url=http://localhost:2368 \
  -v ghost-content:/var/lib/ghost/content \
  ghost:6

For production, run Ghost against MySQL 8 and keep the content directory on a named volume. The following is a Docker Compose file:

services:
  ghost:
    image: ghost:6
    ports: ["2368:2368"]
    volumes: ["ghost-content:/var/lib/ghost/content"]
    environment:
      url: https://example.com
      database__client: mysql
      database__connection__host: db
      database__connection__user: ghost
      database__connection__password: change-me
      database__connection__database: ghost
      logging__transports: '["stdout"]'
  db:
    image: mysql:8
    volumes: ["mysql-data:/var/lib/mysql"]
    environment:
      MYSQL_ROOT_PASSWORD: change-me-too
      MYSQL_DATABASE: ghost
      MYSQL_USER: ghost
      MYSQL_PASSWORD: change-me
volumes: { ghost-content: {}, mysql-data: {} }

Railway replaces the Compose file, reverse proxy and certificate renewal with this template.

How Much Does It Cost to Self-Host Ghost?

The Ghost software is free and MIT licensed: no licence fee, and no cap on posts, members or newsletter sends. Ghost(Pro), the managed service run by the Ghost Foundation, starts at $18/month billed yearly and rises with member count. Self-hosting on Railway costs infrastructure only, a fraction of a members-based plan for a typical blog. Paid memberships need Stripe and newsletters need an SMTP provider; both bill separately.

FAQ

What is Ghost? An open source, MIT-licensed publishing platform built on Node.js. It bundles a CMS, an email newsletter system and paid memberships, and works as a website or headlessly via its Content API.

What does this Railway template deploy? Three services: Ghost from the official ghost:6 image with a volume for uploads and themes, a MySQL 8.4 database on its own volume, and Redis. Ghost is the only service with a public URL.

Why is Redis included, and can I remove it? Redis backs the caches Ghost reads asynchronously — remote image dimensions and theme validation. The settings cache is read synchronously and must stay in memory, which is why adapters__cache__settings__adapter is MemoryCache. For a single-instance blog Redis is optional: unset adapters__cache__active and remove the service.

How do I set up email newsletters in self-hosted Ghost? Set mail__transport=SMTP, mail__options__host, mail__options__port, mail__options__auth__user, mail__options__auth__pass and a mail__from address on a domain you control. Ghost also supports Mailgun's API for bulk sending, configured in Settings → Email newsletter.

How do I add a custom domain to Ghost on Railway? Add the domain to the Ghost service and point DNS as instructed, then update url to https://yourdomain.com. Ghost derives canonical links, feed URLs and redirects from that variable, so it must match the domain you serve.


Template Content

More templates in this category

View Template
Pixelfed
Explore + Share beautiful photos and videos on the Fediverse

Evan Boehs
29
View Template
NEW
Hexo
Self-hosted Hexo blog with editor, comments, and S3 storage

INAPP
1
View Template
NodeBB
Node.js based forum software built for the modern web

Six
65