New blog ...

In 2009, I created legendiary.at as blog platform. Ranging from technology insights, rambling about Linux and Windows problems, and some insights into LEGO building, this contains lots of history.

I've lost track a bit in the past years, also with publishing lots of technical content on the Icinga and NETWAYS blog. Also, Wordpress has become a feature monster. Divi on top makes building websites easier ... but I don't need it.

With the shift to GitHub and GitLab, everything I write is Markdown.

This is a markdown clode block.

Bold and italic.

That's why I was looking into something lightweight with the possibility to write Markdown in the same place. I could have used GitHub/GitLab pages, though I'll keep that for future demo websites in my talks.

So I have found Ghost which has a similar look as Medium, being Open Source and free to use and decided to start fresh at dnsmichi.at.

I'll dive into monitoring/observability, infrastructure as code, development workflows, CI/CD, containers, k8s, serverless and much more.

Enjoy and read you soon!

PS: Did I tell you already that I document everything? Here's the entire setup documentation written in Markdown :-)

dnsmichi.at Ghost blog setup

Goal

Run Ghost as blog in Docker with an Nginx proxy up front, which manages Let's Encrypt TLS certificates.
The setup should be managed with docker-compose.

The Nginx server runs natively on the system, serving other websites on the host too.

Inspired by this article.

Requirements

Ghost requirements.

  • Docker
  • Ghost container
  • MySQL 5.7 (not 8.0+)

Installation steps:

apt install apt-transport-https ca-certificates curl software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"

apt update

apt-cache policy docker-ce

apt install docker-ce

curl -L "https://github.com/docker/compose/releases/download/1.25.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Nginx

apt install nginx
systemctl start nginx.service
systemctl enable nginx.service

Let's Encrypt

add-apt-repository ppa:certbot/certbot
apt install python-certbot-nginx

certbot --nginx -d dnsmichi.at -d www.dnsmichi.at

Ghost Container

mkdir -p /docker/ghost
cd /docker/ghost/
mkdir content mysql

Create docker-compose.yml

vim docker-compose.yml 

version: '3'
services:

  ghost-server:
    image: ghost:latest
    restart: always
    ports:
      - 2368:2368
    depends_on:
      - ghost-db
    environment:
      url: https://dnsmichi.at 
      database__client: mysql
      database__connection__host: ghost-db
      database__connection__user: root
      database__connection__password: your_database_root_password
      database__connection__database: ghost
    volumes:
      - /docker/ghost/content:/var/lib/ghost/content

  ghost-db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: your_database_root_password
    volumes:
      - /docker/ghost/mysql:/var/lib/mysql

Start the stack.

docker-compose up -d

Firewall

ufw allow OpenSSH
ufw allow 'Nginx Full'
systemctl start ufw
ufw enable

For testing, enable 2368 too.

ufw allow 2368/tcp 

ufw status numbered 
ufw delete <rule number> 

fail2ban

apt-get install fail2ban
systemctl enable fail2ban
systemctl start fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

vim /etc/fail2ban/jail.local 

banaction = ufw
vim /etc/fail2ban/jail.d/custom.conf

[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 2
findtime = 120
fail2ban-client reload

fail2ban-client status

Nginx Configuration

vim /etc/nginx/sites-available/dnsmichi.at.conf

server {
    server_name dnsmichi.at www.dnsmichi.at;

    # disable any limits to avoid HTTP 413 for large image uploads
    client_max_body_size 0;

    # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
    chunked_transfer_encoding on;

    location / {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://0.0.0.0:2368/;
            proxy_ssl_session_reuse off;
            proxy_set_header Host $http_host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_read_timeout 900;
            proxy_redirect off;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/dnsmichi.at/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/dnsmichi.at/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

ln -s /etc/nginx/sites-available/dnsmichi.at.conf /etc/nginx/sites-enabled/dnsmichi.at.conf

systemctl reload nginx