---
title: "Deploy Grav"
description: "Self-hosted WordPress alternative that stores content as Markdown files"
category: "CMS"
url: https://railway.com/deploy/grav
---

# Deploy Grav

Self-hosted WordPress alternative that stores content as Markdown files

**[Deploy Grav on Railway](https://railway.com/template/grav)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/grav/manifest.json

- **Creator:** A3A
- **Category:** CMS

## Template content

### Redis https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.svg

- **Image:** redis:8.2
- **Start command:** `/bin/sh -c "rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH"`

### Grav https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grav-light.svg

- **Source:** https://github.com/gridalpha/grav-railway
- **Health check:** /
- **Public domain:** Yes

## Documentation

# Deploy and Host Grav on Railway

Grav is a flat-file CMS: every page is a Markdown file with YAML front matter, every setting is a YAML file, and there is no database to migrate or back up. Themes are Twig templates, plugins hook into a Symfony-based event system, and the site is a directory you edit in the admin panel or your own editor. Teams reach for it when WordPress is more machinery than the job needs but a static site generator is too little — Grav renders on request, so an editor publishes without a build step.

Self-host Grav on Railway and you get the two things a flat-file CMS still needs from a platform: somewhere durable for the files and somewhere fast for the caches. **Grav** serves the site through Apache and PHP 8.4, with a persistent volume at `/var/www/html` — pages, media, configuration, accounts and installed plugins and themes are written there and survive redeploys. **Redis** backs Grav's page, Twig and Flex object caches. Grav holds the public domain; Redis stays private.

![Diagram of the Grav and Redis services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/v1788815845/grav-architecture.png)

## Getting Started with Grav on Railway

Deploy the template, set a password for the administrator account, and open the generated URL. You land on the Quark 2 theme's home page with two sample pages published. Go to `/admin` and sign in with the credentials you supplied at deploy — the account is created inside the container before the web server accepts a request, so nobody can claim the administrator seat first. The username defaults to `admin`.

Then publish something. Choose **Pages → Add Page**, give it a title, pick the *Default* template, set it Published and press Continue. You get a Markdown editor with a formatting toolbar and a media drop zone; write a paragraph, press Save, then click **View site** and open it from the navigation. That round trip confirms the deployment: the page is now a `.md` file on the volume, rendering on the public URL.

Two things are worth checking on day one. The dashboard's **System** panel should read `Cache: redis`, confirming Grav reached the Redis service rather than falling back to file caching. And **Tools → Backups** zips the whole site on to the volume — the backup story for a CMS with no database.

![Grav admin dashboard listing pages, plugins and the Redis cache](https://res.cloudinary.com/rroe4rtk/image/upload/v1788815847/grav-admin-dashboard.png)

![Editing the Release Notes page in Grav's Markdown editor](https://res.cloudinary.com/rroe4rtk/image/upload/v1788815849/grav-page-editor.png)

![The published Release Notes page on the Grav site](https://res.cloudinary.com/rroe4rtk/image/upload/v1788815850/grav-site-release-notes.png)

## About Hosting Grav

Grav's design decision is that the filesystem is the database. A page lives at `user/pages/01.home/default.md`; the folder name sets the URL and ordering, the front matter the metadata, the body is Markdown. Configuration is the same — `user/config/system.yaml` is plain YAML you can read, diff and commit. Copy the directory and you have copied the site, which is not true of a database-backed CMS.

Key features:

- Markdown pages with a rich editor, media handling and image processing built in
- Twig theming, so a designer can build a theme without touching PHP
- A plugin and theme marketplace installable from the panel or the `bin/gpm` CLI
- Multi-language content, taxonomy, forms and a scheduler for recurring jobs
- A REST API and a modern admin panel (Admin 2) in the box

On Railway that becomes two services. Grav renders and owns the volume — not an optimisation here, it *is* the site, so removing it loses everything an editor publishes. Redis is the cache tier: Grav's file cache would otherwise sit on that same volume, so moving it keeps rendering off network storage.

## Why Deploy Grav on Railway

Railway removes the parts of self-hosting Grav that are not about the CMS itself:

- A persistent volume for the whole site, provisioned and mounted for you
- Managed Redis on a private network, with no connection strings to copy
- HTTPS and a public domain on first deploy; custom domains are a DNS record away
- PHP 8.4, Apache and every extension Grav needs already in the image
- Rebuilds from a public GitHub repo, so you can fork the stack

## Common Use Cases

- **Documentation and handbooks** — Markdown source, Git-friendly, no database
- **Agency and brochure sites** — a designer builds a Twig theme, the client edits it
- **Developer blogs and changelogs** — write a post as a file, publish with no rebuild

## Dependencies for Grav

- **Grav** — built from [gridalpha/grav-railway](https://github.com/gridalpha/grav-railway) on the official [`getgrav/grav`](https://hub.docker.com/r/getgrav/grav) image (PHP 8.4 + Apache). The release is baked in and copied on to the volume at first boot.
- **Redis** — Railway's managed Redis, wired in as `system.cache.driver: redis`.

### Environment Variables Reference

| Variable | Purpose |
|---|---|
| `GRAV_ADMIN_USER` | Administrator username: 3–16 lowercase letters, digits, `-`, `_` |
| `GRAV_ADMIN_PASSWORD` | Administrator password: 8+ characters with an uppercase letter, a lowercase letter and a digit |
| `GRAV_ADMIN_EMAIL` | Address on the administrator account, used for password recovery |
| `GRAV_SITE_TITLE` | Site title written into `user/config/site.yaml` on first boot |
| `GRAV_REDIS_HOST` | Redis host; leave the reference in place to use the Redis service |
| `GRAV_REDIS_PORT` | Redis port |
| `GRAV_REDIS_PASSWORD` | Redis password |
| `GRAV_REDIS_DATABASE` | Redis database number, `0` by default |
| `PORT` | Port Apache listens on |

These settings apply **only while the volume has no site on it**. After that the config files are yours — the admin panel writes them — so changing the variables later is intentionally a no-op. Change the password in **Users** and the cache driver in **Configuration → System**.

### Deployment Dependencies

- Source repository: https://github.com/gridalpha/grav-railway
- Upstream project: https://github.com/getgrav/grav
- Official image: https://hub.docker.com/r/getgrav/grav
- Docs: https://learn.getgrav.org

## Hardware Requirements for Self-Hosting Grav

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU | 1–2 vCPU |
| RAM | 512 MB | 1 GB |
| Storage | 5 GB volume | 5–20 GB, depending on media |
| Runtime | PHP 8.3+ with `gd`, `intl`, `zip` | PHP 8.4 with `apcu`, `yaml`, `redis` |

Grav is light: a site with a few hundred pages runs comfortably in 512 MB. The storage figure is really about media, since uploaded images and their derivatives are the only things that grow without bound.

## Self-Hosting Grav with Docker

The official image downloads Grav on first boot into whatever you mount at `/var/www/html`, so a named volume is all it needs:

```
docker run -d --name grav \
  -p 8080:80 \
  -v grav_site:/var/www/html \
  getgrav/grav:php8.4
```

Open `http://localhost:8080/admin` and create the first account through the setup screen. To build the image this template uses — Apache on `$PORT`, the release baked in, the administrator created before the port opens:

```
git clone https://github.com/gridalpha/grav-railway.git
cd grav-railway
docker build -t grav-railway .
docker run -d -p 8080:8080 -v grav_site:/var/www/html \
  -e PORT=8080 -e GRAV_ADMIN_PASSWORD='ChangeMe123' grav-railway
```

Without Docker, Grav is a PHP application you unzip: download `grav-admin` from getgrav.org, point a PHP 8.3+ web server at the directory with `AllowOverride All` so its `.htaccess` is honoured, and make `user/`, `cache/`, `logs/` and `images/` writable by the web server user.

## Is Grav Free to Self-Host?

Grav is open source under the MIT licence, and that covers the admin panel, the REST API and the whole plugin and theme marketplace — no paid edition, no seat count, no feature gate. Trilby Media sells optional premium plugins (a forum, collaborative editing, revision history), but nothing in the core needs them. On Railway you pay only for the compute, volume and Redis you use.

## FAQ

**What is Grav?**

An open-source flat-file CMS written in PHP. It stores pages as Markdown and configuration as YAML, with no database, and renders through Twig on request.

**What does this Railway template deploy?**

Two services: Grav on Apache with PHP 8.4, holding a persistent volume at `/var/www/html`, and Railway's managed Redis as the cache backend. Grav gets the public domain; Redis stays private.

**Why does a database-free CMS need Redis?**

It does not need one, but it benefits. Grav caches parsed pages, compiled Twig templates and Flex objects, and by default those land on the same volume as the content. Redis keeps rendering off network storage and lets you flush the cache without touching files.

**How do I log in to Grav for the first time?**

Go to `/admin` with the `GRAV_ADMIN_USER` and `GRAV_ADMIN_PASSWORD` values you set at deploy. The account is created before the web server starts, so the first-run setup screen is already closed when the site becomes reachable.

**How do I install plugins and themes on a self-hosted Grav?**

Use **Plugins → Add** or **Themes → Add**; packages install straight on to the volume and survive redeploys. `bin/gpm install ` does the same from a shell.

**How do I upgrade Grav after deploying?**

Update from **Tools → Updates** — Grav updates itself in place on the volume, as on any host. Rebuilding the image never overwrites an existing site: the baked release only seeds an empty volume. A custom domain needs no config change either, since the site is not pinned to a hostname.



## Similar templates

- [Libredesk - Complete Setup](https://railway.com/deploy/libredesk-complete-setup) — Complete self-hosted omnichannel customer support desk.
- [Paperless-ngx](https://railway.com/deploy/paperless-ngx-3) — Paperless-ngx — document management with OCR and full-text search
- [Instatic CMS - Postgres](https://railway.com/deploy/instatic-cms-postgres) — Design, build and manage powerful static sites from state-of-the-art CMS

Open this page in a browser: https://railway.com/deploy/grav
