How to Self-Host Grav CMS with Docker Compose
What Is Grav?
Grav is a flat-file CMS built on PHP that stores everything — pages, configuration, user accounts, media — as files on disk instead of in a database. No MySQL, no PostgreSQL, no database maintenance. Your entire site lives in a single directory that you can back up by copying it.
Grav uses Markdown for content, YAML for configuration, and Twig for templating. It includes a built-in admin panel with a visual editor, a package manager (GPM) for installing plugins and themes, and multi-language support. It’s lighter and simpler than WordPress while being more feature-rich than static site generators like Hugo.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 512 MB of RAM (256 MB minimum for small sites)
- A domain name (optional, for remote access)
Docker Compose Configuration
Grav is a single-container application with no database dependency. The LinuxServer.io image includes PHP, Nginx, and the Grav Admin plugin pre-installed.
Create a docker-compose.yml file:
services:
grav:
image: lscr.io/linuxserver/grav:1.7.49.5
container_name: grav
restart: unless-stopped
environment:
- PUID=1000 # User ID for file permissions
- PGID=1000 # Group ID for file permissions
- TZ=Etc/UTC # Your timezone (e.g., America/New_York)
volumes:
- grav-config:/config
ports:
- "8080:80"
volumes:
grav-config:
Start the stack:
docker compose up -d
Access Grav at http://your-server-ip:8080.
Initial Setup
- Navigate to the admin panel at
http://your-server-ip:8080/admin. - Create your admin account. On first access, Grav prompts you to set up the initial administrator. Choose a strong password — there’s no password recovery without file access.
- Configure the site title and basic settings in Admin → Configuration → Site.
- Choose a theme in Admin → Themes. The default Quark theme is clean and responsive, but hundreds of alternatives are available via GPM.
Alternative: Create Admin via CLI
If the web-based admin creation doesn’t appear:
docker exec -it grav bin/plugin login new-user
Follow the prompts to create a username, password, email, and set the user’s permissions to admin.
Configuration
Site Configuration
Grav stores all configuration in YAML files under /config/user/config/. The main settings live in system.yaml and site.yaml.
Edit via the admin panel (Configuration → System/Site) or directly in the files:
# Access the configuration directory
docker exec -it grav ls /config/user/config/
Key settings in system.yaml:
| Setting | Default | Description |
|---|---|---|
pages.theme | quark | Active theme |
cache.enabled | true | Page caching (disable for development) |
languages.supported | [] | Multi-language support |
debugger.enabled | false | Debug toolbar |
images.default_image_quality | 85 | JPEG quality for resized images |
Installing Plugins
Use the admin panel (Plugins → Add) or the CLI:
# Install a plugin via GPM
docker exec -it grav bin/gpm install shortcode-core
# Popular plugins:
# - admin (pre-installed)
# - sitemap — generates XML sitemap
# - feed — generates RSS/Atom feeds
# - pagination — page list pagination
# - breadcrumbs — navigation breadcrumbs
# - markdown-notices — callout boxes in Markdown
Installing Themes
docker exec -it grav bin/gpm install theme-name
Activate a new theme via Admin → Themes or by editing system.yaml:
pages:
theme: theme-name
Advanced Configuration (Optional)
Email Configuration
For contact forms and password resets, configure SMTP in the Email plugin settings (Admin → Plugins → Email):
# In user/config/plugins/email.yaml
enabled: true
from: you@example.com
mailer:
engine: smtp
smtp:
server: smtp.example.com
port: 587
encryption: tls
user: you@example.com
password: your-password
Multi-Language Support
Grav supports multiple languages natively. Enable in system.yaml:
languages:
supported:
- en
- es
- fr
default_lang: en
include_default_lang: true
Create translated pages by adding language suffixes: default.en.md, default.es.md, default.fr.md.
Custom Page Types
Grav’s page structure is file-based. Create new content types by adding Twig templates to your theme:
user/themes/quark/templates/
├── default.html.twig # Standard pages
├── blog.html.twig # Blog listing
├── blog_item.html.twig # Blog posts
├── modular.html.twig # Modular pages
└── portfolio.html.twig # Custom type
Reverse Proxy
The container runs Nginx on port 80 (mapped to 8080 in the example above). For HTTPS in production, use a reverse proxy.
For Nginx Proxy Manager, create a proxy host pointing to port 8080 with SSL enabled.
CloudFlare users: Disable Rocket Loader globally or for /admin paths — it breaks the admin panel’s JavaScript scrolling.
See Reverse Proxy Setup for detailed instructions.
Backup
Grav’s flat-file architecture makes backups simple. Everything lives in the /config volume:
# Built-in backup command
docker exec -it grav bin/grav backup
# Or back up the entire volume
docker run --rm -v grav-config:/data -v $(pwd):/backup alpine tar czf /backup/grav-backup.tar.gz -C /data .
Restore by extracting the archive back to the volume. No database dumps or restoration procedures — just files.
See Backup Strategy for automated approaches.
Troubleshooting
Admin Panel Not Loading
Symptom: /admin returns a 404 or blank page.
Fix: The admin plugin may not be installed. Run: docker exec -it grav bin/gpm install admin. If GPM can’t connect (SSL issues behind a proxy), check system.gpm.verify_peer in configuration.
GPM “Fetch Failed” Errors
Symptom: Plugin/theme installation fails with network errors.
Fix: GPM requires outbound HTTPS access to getgrav.org. If behind a corporate firewall or restrictive network, ensure port 443 outbound is open. Alternatively, download plugins manually from the Grav website and extract them to /config/user/plugins/.
File Permission Errors
Symptom: “Unable to write file” or “Permission denied” in logs.
Fix: Check that PUID and PGID environment variables match your host user. The container runs as this user for file operations. Update the values and recreate the container: docker compose up -d --force-recreate grav
Admin Panel Scrolling Broken
Symptom: Admin panel content doesn’t scroll on some pages. Fix: If using CloudFlare, disable Rocket Loader (Speed → Optimization → Rocket Loader). Clear your browser cache. This is a known conflict with Grav’s admin JavaScript.
Slow Page Loads
Symptom: Pages take 2+ seconds to load. Fix: Enable caching in Admin → Configuration → System → Caching → Enabled. Grav compiles Twig templates and caches processed Markdown. First load after clearing cache is slow; subsequent loads should be under 200ms.
Resource Requirements
- RAM: 256 MB idle, 512 MB under load with admin panel active
- CPU: Low (single core sufficient for small to medium sites)
- Disk: 100 MB for Grav core + plugins + themes, plus your content and media
Verdict
Grav is the best flat-file CMS for self-hosting. The zero-database architecture eliminates an entire category of maintenance, backup complexity, and failure modes. The admin panel provides a user-friendly editing experience, and GPM handles plugin/theme management cleanly.
Choose Grav over WordPress if you want simplicity and don’t need WordPress’s massive plugin ecosystem. Choose Grav over Hugo if you want an admin panel and dynamic features without static site generation. For database-backed CMS power with more features, stick with WordPress. For pure speed and simplicity with no server-side processing, choose Hugo.
FAQ
Does Grav need a database?
No. Grav is a flat-file CMS that stores everything — pages, config, users — as files on disk. No MySQL, PostgreSQL, or SQLite required. Your entire site lives in one directory.
Can I migrate from WordPress to Grav?
Yes. Grav has a WordPress import plugin that converts posts and pages to Markdown. Media files transfer manually. The main effort is re-creating your theme — Grav uses Twig templating instead of PHP templates.
How does Grav compare to Hugo?
Grav runs a PHP server and generates pages dynamically (with caching), while Hugo generates static HTML at build time. Grav has an admin panel and plugin system; Hugo is faster but requires rebuilds for every content change. Choose Grav for ease of editing, Hugo for maximum performance.
Is Grav suitable for large sites?
Grav handles hundreds of pages well with caching enabled. For sites with thousands of pages, performance degrades because it scans the filesystem on cache rebuilds. At that scale, consider a database-backed CMS like WordPress or a static generator like Hugo.
Can multiple users edit content in Grav?
Yes. Grav supports multiple user accounts with role-based permissions through the admin panel. You can create editor, author, and admin roles. For team collaboration, enable the admin plugin’s two-factor authentication.
Related
- Grav vs Hugo: Flat-File CMS or Static Generator?
- Grav vs October CMS: PHP Content Systems Compared
- Ghost vs WordPress: Which Should You Self-Host?
- How to Self-Host WordPress
- How to Self-Host Ghost
- How to Self-Host Hugo
- Best Self-Hosted CMS
- Self-Hosted Alternatives to WordPress.com
- Docker Compose Basics
- Reverse Proxy Setup
- Backup Strategy
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments