How to Install Grav CMS on Debian 11

Updated on January 19, 2022
How to Install Grav CMS on Debian 11 header image


Grav is an open-source flat-file CMS written in PHP. This guide will show you how to install Grav on a fresh Debian 11 Vultr instance with an Nginx/PHP-FPM web server and secure it with a Let's Encrypt TLS certificate. Grav CMS does not require a database like MySQL or MariaDB

You'll use a standard, non-root user for Grav CMS to create the Markdown content.


  • A fresh Debian 11 Vultr instance
    • This guide was tested on a 2048 MB High Frequency, 1 vCPU instance.
  • A domain name pointing to your server IP addresses
  • A non-root user
  • Be familiar with using the Linux command line
  • Know how to use vi, nano, or another text editor

In this guide, the domain name is and The non-root user name is johndoe. Replace these names with your actual domain and user names.

Verify that your domain name is accessible from a different server.

$ ping -c1
PING ( 56 data bytes
64 bytes from icmp_seq=0 ttl=57 time=5.163 ms

$ ping -c1
PING ( 56(84) bytes of data.
64 bytes from ( icmp_seq=1 ttl=58 time=11.1 ms

If you have configured IPv6 for your domain, verify that the server also accessible over IPv6.

$ ping -6 -c1
PING (2001:0db8:220:1:248:1893:25c8:1946)) 56 data bytes
64 bytes from 2001:0db8:220:1:248:1893:25c8:1946 (2001:0db8:220:1:248:1893:25c8:1946): icmp_seq=1 ttl=58 time=9.92 ms

$ ping -6 -c1
PING (2001:0db8:220:1:248:1893:25c8:1946)) 56 data bytes
64 bytes from 2001:0db8:220:1:248:1893:25c8:1946 (2001:0db8:220:1:248:1893:25c8:1946): icmp_seq=1 ttl=58 time=10.2 ms

Before You Begin

Login as root and verify your Debian version.

# lsb_release -ds
Debian GNU/Linux 11 (bullseye)

Ensure that your system is up to date.

# apt update && apt upgrade

Install required and useful packages.

# apt install unzip pwgen

Use pwgen to generate a secure password.

# pwgen -s 20 1

Create a non-root user account with sudo access. Use the password you created in the previous step.

# adduser johndoe --gecos "John Doe"
# usermod -aG sudo johndoe

Switch to the new user account.

# su - johndoe

Note: If desired, you can generate and install an SSH Key, then SSH into the server as johndoe without a password. For the rest of this guide, make sure you are logged in as your non-root user.

If you want to change the timezone from UTC, use this command to reconfigure your timezone.

$ sudo dpkg-reconfigure tzdata

Verify that the UFW firewall is active.

$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22                         ALLOW       Anywhere

Note: UFW should already be active on a new Vultr Debian 11 instance. If you are using Firewalld as a replacement for UFW, then you use firewall-cmd to manage your firewall instead, as shown:

$ sudo firewall-cmd --state
[sudo] password for george: 

1. Install Nginx and Required Extensions

Install the Nginx web server and the required PHP extensions for a Grav CMS installation:

$ sudo apt install nginx php-fpm php-cli php-apcu php-curl php-gd php-mbstring php-xml php-yaml php-zip

Verify the Nginx and PHP installation.

$ sudo nginx -v
nginx version: nginx/1.18.0

$ sudo php -v
PHP 7.4.25 (cli) (built: Oct 23 2021 21:53:50) ( NTS )

2. Open the Nginx Ports

If you use UFW, then use the ufw command to open the HTTP & HTTPS ports.

$ sudo ufw allow 'Nginx Full'

Or, if you are using Firewalld, then use firewall-cmd to open the ports.

$ sudo firewall-cmd --permanent --zone=public --add-service={http,https}

$ sudo firewall-cmd --reload

In your web browser, enter:

You should see the Welcome to Nginx! message.

If not, tail the Nginx error log, try again, and the error log file will likely point you in the right direction.

$ sudo tail -f /var/log/nginx/error.log

3. Create the PHP-FPM Configuration

Create a local PHP-FPM configuration file to update the php.ini settings. These are useful settings for a production-grade Grav CMS installation. You may use the same file to customize other PHP configuration settings.

$ sudo nano /etc/php/7.4/fpm/conf.d/00-local.ini

Add the following:

post_max_size = 64M
upload_max_filesize = 64M
memory_limit = 256M
max_execution_time = 300
max_input_vars = 1540
date.timezone = "America/Chicago"

Save and close the file.

Remove the default PHP-FPM pool configuration and create a PHP-FPM pool configuration for our johndoe user.

$ sudo rm /etc/php/7.4/fpm/pool.d/www.conf
$ sudo nano /etc/php/7.4/fpm/pool.d/johndoe.conf

Edit the file as shown.


user = johndoe
group = johndoe

listen = /var/run/php/php7.4-fpm.sock

listen.owner = www-data = www-data

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

chdir = /

Save and close the file.

Create a PHP test file for the johndoe user.

$ mkdir -p ~/www/
$ nano ~/www/

Paste the following:


Save and close the file.

4. Create the Nginx Site Configuration

Create a new site configuration. Replace with your domain name.

$ sudo nano /etc/nginx/sites-available/

Paste the following, replacing with your domain name.

server {
    listen 80;

    root /home/johndoe/www/;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;

    # deny all direct access for these folders
    location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; }

    # deny running scripts inside core system folders
    location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }

    # deny running scripts inside user folder
    location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }

    # deny access to specific files in the root folder
    location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;

Save and close the file.

Enable the new site and disable the default site.

$ sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled
$ sudo rm /etc/nginx/sites-enabled/default

Check for errors in the Nginx configuration.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

5. Test the Configuration

Restart the nginx and php-fpm services.

$ sudo systemctl restart nginx php7.4-fpm

In your web browser, enter:

You should see the PHP information page displayed, starting with the installed PHP version.

If it is not displayed, check the nginx and php-fpm log files.

$ sudo tail -f /var/log/nginx/error.log
$ sudo tail -f /var/log/php7.4-fpm.log

6. Secure with a Let's Encrypt TLS Certificate

Install the Let's Encrypt certbot package for Nginx and request a new TLS certificate.

$ sudo apt install python3-certbot-nginx
$ sudo certbot --nginx -d -d

You will answer a few questions, and then a request for a TLS certificate is made. The questions include:

  • An email address for urgent renewal/security notices.
  • Agreeing to the Terms of Service.
  • Sharing your email address with the Electronic Frontier Foundation.

Look for the following line in the output:

Congratulations! You have successfully enabled and

Certbot also adds TSL support to your Nginx configuration and restarts the Nginx service.

To test your new TLS configuration, enter the following in your web browser:

Certbot automatically added a schedule to renew the certificate. You can test the renewal process with:

$ sudo certbot --dry-run renew

Look for the following at the end of the command output:

Congratulations, all simulated renewals succeeded: 
  /etc/letsencrypt/live/ (success)

A system timer runs twice a day to request a certificate renewal. You can verify that the certbot timer is running by doing:

$ systemctl list-timers | egrep 'NEXT|certbot'
NEXT                        LEFT        LAST                        PASSED       UNIT                         ACTIVATES
Thu 2021-12-09 02:29:37 CST 13h left    Wed 2021-12-08 12:32:29 CST 45min ago    certbot.timer                certbot.service

7. Install Grav CMS with the Admin Plugin

We are now ready to install the Grav CMS. First, we will install the version that includes the Administration Panel. Do the following:

$ cd ~/www/
$ wget -O
$ unzip -q
$ rm -rf html
$ mv grav-admin html

In your web browser, enter:

You will automatically be redirected to the first time you access your site domain address. Next, you need to create your first user for the Admin account.

  1. Enter an Admin user name, email address, password, full name, and title.
  2. Click Create User. You will be logged into your Grav Administration Panel.
  3. Look around, then log out.
  4. Access your site domain again. You should see "installation successful...".
  5. Verify that the Typography menu link works. If you see a 404 Error, follow the referenced troubleshooting guide.

You are now ready to create content. Follow the introduction steps displayed on the Say Hello to Grav! page. You can access the Admin panel at

More Information

You can refer to these links for more information about the Grav CMS.