Ever since I bought my domain hexf.me I have always wanted to use it for a blog, to use it as an information outlet, to teach people. Previously I used solutions such as Hugo and Jekyll with some CI work flow to upload my pages, but while it was a nice use of Git and CI/CD, it wasnt very practical. When I set out to revamp my website, I had a few goals in mind. Firstly, I wanted it to not require a database, as that is extra. Secondly, I wanted it to have an easy place for me to write with technologies I already know, like Markdown. Finally, I wanted it to be extensible and allow me to use LDAP to authenticate to join my SSO network.

I went looking for quite a while, but eventually fell in love with Grav. Grav provides everything I need, it is a flat-file CMS which allows me to write in Markdown and has a wonderful assortment of plugins like an LDAP authentication plugin. Grav has an admin panel plugin which allows me to write blog posts, and update the page content all from the browser, which is great.

After selecting Grav, I moved onto writing up the role in my ansible playbook. Grav is a very simple role, basically it downloads all the php extension we require, downloads and installs Grav into /var/www/grav and finally installs NGINX config and restarts NGINX, simple as that.

---
- name: Install php7.4-fpm and extensions
  apt:
    state: present
    name:
      - php7.4-fpm
      - unzip
      - php7.4-curl
      - php7.4-ctype
      - php7.4-dom
      - php7.4-gd
      - php7.4-json
      - php7.4-mbstring
      - php7.4-simplexml
      - php7.4-xml
      - php7.4-zip
      - php7.4-apcu
      - php7.4-opcache
      - php7.4-yaml
      - php7.4-ldap

  when: ansible_facts['os_family']|lower == "debian"

- name: Make dir for Grav
  file:
    state: directory
    path: /var/www/grav/
    owner: www-data
    group: www-data
    mode: 'u=rwX,g=rX,o=rX'
- name: Check if Grav is installed
  stat:
    path: /var/www/grav/grav-admin/
  register: grav_admin
- name: Download & Extract Grav
  unarchive:
    src: https://getgrav.org/download/core/grav-admin/latest
    dest: /var/www/grav/
    remote_src: yes
    owner: www-data
    group: www-data
    mode: 'u=rwX,g=rX,o=rX'
  when: grav_admin.stat.exists == False

- name: Grav NGINX config
  template:
    src: server_grav.conf.j2
    dest: /etc/nginx/conf/server_grav.conf
    owner: root
    group: root
    mode: '0744'
  notify: restart nginx

As Grav depends on NGINX, It would be appropriate to talk about the NGINX role. The main task for NGINX is very simple, it calls upon 2 other task files, debian.yaml and nginx_config.yaml

---
- name: Debian Based Systems
  import_tasks: debian.yaml
  when: ansible_facts['os_family']|lower == "debian"
- name: Configure NGINX
  import_tasks: nginx_config.yaml

debian.yaml is responsible for installing nginx and fcgiwrap. We use fcgiwrap for later our git deployment, to install cgit, more on this later.

---
- name: "Install nginx"
  apt:
    name: nginx
    state: present
- name: "Install fcgiwrap"
  apt:
    name: fcgiwrap
    state: present

The nginx_config.yaml basically just templates our nginx config files

---
- name: "Write Main Config"
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0744'
  notify: restart nginx
- name: "Create folder for sub-configs"
  file:
    path: /etc/nginx/conf
    state: directory
    mode: '0755'
- name: "Base Server Config"
  template:
    src: _base_server.conf.j2
    dest: /etc/nginx/conf/_base_server.conf
    owner: root
    group: root
    mode: '0744'
- name: "Catchall Server Config"
  template:
    src: server_catchall.conf.j2
    dest: /etc/nginx/conf/server_catchall.conf
    owner: root
    group: root
    mode: '0744'
  notify: restart nginx 

The config templates are just standard NGINX configuration

#nginx.conf.j2
worker_processes    1;
user www-data www-data;
events {
    worker_connections  1024;
}

http {
    include         mime.types;
    default_type        application/octet-stream;
    sendfile        on;
    keepalive_timeout   65;
    gzip            on;

    index index.html index.htm index.php;

    error_log syslog:server=unix:/dev/log warn;
    access_log syslog:server=unix:/dev/log;

    ssl_certificate     /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;

    include         conf/server*.conf;

}
#_base_server.conf.j2
listen 443 ssl;
ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers         HIGH:!aNULL:!MD5;
listen 80;

if ($scheme != "https") {
    return 301 https://$host$request_uri;
}
server {
  server_name _;

  server_name_in_redirect off;
  root /var/www/default/htdocs;
  # include conf/_base_server.conf;
  listen 80 default_server;
}

Now, the nginx role depends on certbot for LetsEncrypt certificates, but previously I explained that, so I won't go into it here.

The next task is actually setting up Grav. For this you just web browse to your server and will be greeted with a screen asking you to put in credentials. Then its just a matter of digging through all the menus in the Configuration and Themes tabs to configure your website. After that, open the pages section and get writing!

In conclusion, Grav is a wonderful PHP based flat-file CMS for building your own website with, blogging and much more.

Previous Post Next Post