How to Install Grav CMS on Debian 11

Author: George Wilder

Last Updated: Wed, Jan 19, 2022
CMS Debian


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.

Want to contribute?

You could earn up to $600 by adding new articles.