How To Install Grav CMS Using Docker Swarm

Docker Oct 19, 2021

You'll need a server, docker swarm, ssl and nginx installed.

Create a folder and cd into it.

Create a Dockerfile:

FROM php:7.4-fpm

ENV DEBIAN_FRONTEND noninteractive

# Get the basic stuff
RUN apt-get update && \
    apt-get -y upgrade && \
    apt-get install -y \
    sudo net-tools procps nano \
    libgmp-dev \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    iputils-ping \
    libxml2-dev \
    unzip \
    libonig-dev \
    libzip-dev \
    libsodium-dev \
    libonig-dev



RUN  docker-php-ext-install \
     pdo \
     pdo_mysql \
     mysqli \
     mbstring \
     tokenizer \
     xml \
     ctype \
     json \
     zip \
     intl \
     bcmath \
     sodium \
     sockets \
     gd \
     exif

RUN apt-get update && \
    apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev

RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
    docker-php-ext-install gd

# Create user with sudo privileges
RUN useradd -ms /bin/bash -u 1000 durbok && \
    usermod -aG sudo durbok
# New added for disable sudo password
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# Set as default user
USER durbok
WORKDIR /var/www/html

ENV DEBIAN_FRONTEND teletype

CMD ["php-fpm"]

Build docker image:

docker build -t your_docker_image_name:tag .

Push docker image to your docker repo if you have it. You can use Docker hub or a private repo.

Create docker-compose.yml:

version: "3.7"

services:

  prod:
    image: ur_built_image_here:php:7.4-fpm
    networks:
      - durbok-net
    deploy:
      placement:
        constraints:
        - node.role == manager
      replicas: 1
      restart_policy:
        condition: on-failure
    volumes:
      - ./grav-admin:/var/www/html
networks:
  durbok-net:
    external: true

Create a folder for grav code (grav-admin in my case). Cd in to it, then download zip from https://getgrav.org/downloads and unzip it. You can remove *.zip afterwards.

Create a script (I've called it chmod.sh):

#!/bin/sh
chown -R $USER:www-data .
find . -type f -exec chmod 664 {} \;
find ./bin -type f -exec chmod 775 {} \;
find . -type d -exec chmod 775 {} \;
find . -type d -exec chmod +s {} \;

Deploy grav:

docker stack deploy -c docker-compose.yml --with-registry-auth chose_name

Enter docker container and run chmod.sh.

docker exec -it container_name bash
sudo sh chmod.sh
exit

In case php-fpm didn't start you can run manually inside container:

php-fpm &

Nginx - I am also using docker for nginx which is also in a swarm mode.
Example:

version: '3.7'

services:
  prod:
    image: nginx:stable-alpine
    volumes:
      - ./nginx-conf:/etc/nginx/conf.d
      - ./ssl:/etc/nginx/ssl
      - ./webfolder:/var/www/html/webfolder
    networks:
      - durbok-net
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      restart_policy:
        condition: on-failure
    ports:
      - 80:80
      - 443:443

networks:
  durbok-net:
    external: true

volumes:
  nginx-conf:

Nginx somename.conf:

server {
  listen 80;
  listen [::]:80;
  server_name urdomain.dev;
  rewrite ^ https://$http_host$request_uri? permanent;
}

server {

  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name urdomain.dev;
  root /var/www/html/webfolder/grav-admin;
  try_files $uri $uri/ /index.php;

  index index.html index.php;
  client_max_body_size 256M;


  error_log  /var/log/nginx/grav_error.log;
  access_log /var/log/nginx/grav_access.log;


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


  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass grav_prod:9000; # this is docker service name 
    fastcgi_index index.php;
    include fastcgi_params;
    ## this is tricky part ### after scipt_filename I had to put /var/www/html cause that's where my code was in php-fpm docker container
    fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
  }


  location = /favicon.ico {
    log_not_found off;
    access_log off;
  }

  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
  }

  location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 365d;
  }

  location ~*  \.(pdf)$ {
    expires 30d;
  }

  location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 90d;
    add_header Cache-Control "public, no-transform";
  }


location ~ /\.(?!well-known).* {
    deny all;
    access_log off;
    log_not_found off;
}

  add_header Content-Security-Policy upgrade-insecure-requests;

  ssl_certificate /etc/nginx/ssl/ur.pem;
  ssl_certificate_key /etc/nginx/ssl/ur.key;
#  ssl_dhparam /etc/nginx/ssl/dhparams.pem;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:5m;


  #SSL Security
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
  #XP and IE6 support
  #ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
  ssl_ecdh_curve secp384r1;
  ssl_prefer_server_ciphers on;
  ssl_session_tickets off;

  proxy_set_header X-Forwarded-For $remote_addr;
  
  #Compress and optimize delivery of files


  gzip on;
  gzip_comp_level    5;
  gzip_min_length    256;
  gzip_vary          on;
  gzip_types
    application/atom+xml
    application/javascript
    application/json
    application/ld+json
    application/manifest+json
    application/rss+xml
    application/vnd.geo+json
    application/vnd.ms-fontobject
    application/x-font-ttf
    application/x-web-app-manifest+json
    application/xhtml+xml
    application/xml
    font/opentype
    image/bmp
    image/svg+xml
    image/x-icon
    text/cache-manifest
    text/css
    text/plain
    text/vcard
    text/vnd.rim.location.xloc
    text/vtt
    text/x-component
    text/x-cross-domain-policy;
    # text/html is always compressed by gzip module

}
nginx -s reload

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.