Docker Homelab Beginner's Guide

Created using Perplexity AI

Rocky Linux 9 Docker Deployment Guide

Complete Beginner’s Guide to Docker Services on Proxmox

Table of Contents

  1. Prerequisites
  2. VM Hardware Recommendations
  3. Rocky Linux 9 VM Setup
  4. System Preparation
  5. Docker Installation
  6. User and Group Configuration
  7. Firewall Configuration
  8. Directory Structure Setup
  9. Docker Compose Configuration
  10. Service Configuration
  11. Deployment
  12. Post-Deployment Tasks
  13. Troubleshooting
  14. Tips and Reminders

Prerequisites

Before beginning this deployment, ensure you have:

VM Hardware Recommendations

Minimum Specifications

Storage Breakdown

⚠️ Important: Rocky Linux 9 requires x86-64-v2 CPU features. In Proxmox, use “host” CPU type or ensure your CPU type supports AVX2 instructions.

Rocky Linux 9 VM Setup

Proxmox VM Creation

  1. Create new VM in Proxmox
  2. OS: Linux (6.x/2.6 Kernel)
  3. CPU: Set to “host” type with 4+ cores
  4. Memory: 8GB minimum
  5. Storage: 60GB+ on fast storage
  6. Network: Default bridge with DHCP or static IP

Rocky Linux 9 Installation

  1. Boot from ISO and select minimal installation
  2. Configure network with static IP (recommended)
  3. Create user account with sudo privileges
  4. Complete installation and reboot

System Preparation

Update System

# Update all packages
sudo dnf update -y

# Reboot to ensure kernel updates are active
sudo reboot

Install Essential Packages

# Install required utilities
sudo dnf install -y epel-release
sudo dnf install -y curl wget git nano vim htop tree

# Install development tools (optional but useful)
sudo dnf groupinstall -y "Development Tools"

Set Timezone (Optional)

# Set your timezone
sudo timedatectl set-timezone America/Chicago
# Verify
timedatectl

Docker Installation

Remove Conflicting Packages

# Remove podman and buildah if installed
sudo dnf remove -y podman buildah

Add Docker Repository

# Install dnf config manager
sudo dnf install -y dnf-utils

# Add Docker repository
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo

Install Docker

# Install Docker and related packages
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# If you encounter containerd.io conflicts, use:
# sudo dnf install docker-ce --allowerasing -y

Start and Enable Docker

# Start Docker service
sudo systemctl start docker

# Enable Docker to start on boot
sudo systemctl enable docker

# Verify Docker is running
sudo systemctl status docker

Verify Installation

# Check Docker version
docker --version

# Check Docker Compose version
docker compose version

# Test Docker installation
sudo docker run hello-world

User and Group Configuration

Add User to Docker Group

# Add current user to docker group
sudo usermod -aG docker $USER

# Apply group changes without logout
newgrp docker

# Verify group membership
groups $USER
# Create a dedicated user for Docker services
sudo useradd -r -s /bin/false -d /data dockersvc

# Add dockersvc user to docker group
sudo usermod -aG docker dockersvc

Set Proper Permissions

# Ensure docker.sock has correct permissions
sudo chmod 666 /var/run/docker.sock

# Fix ownership if needed
sudo chown root:docker /var/run/docker.sock

Firewall Configuration

Basic Firewall Setup

# Start and enable firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld

Configure Docker Firewall Integration

# Add docker0 interface to trusted zone
sudo firewall-cmd --permanent --zone=trusted --add-interface=docker0

# Enable masquerading for Docker networking
sudo firewall-cmd --permanent --zone=public --add-masquerade

# Create a custom zone for Docker services (optional)
sudo firewall-cmd --permanent --new-zone=docker-services
sudo firewall-cmd --permanent --zone=docker-services --set-target=ACCEPT

Open Required Ports

# Homepage (Port 3000)
sudo firewall-cmd --permanent --zone=public --add-port=3000/tcp

# OpenSpeedTest (Ports 3001-3002)
sudo firewall-cmd --permanent --zone=public --add-port=3001/tcp
sudo firewall-cmd --permanent --zone=public --add-port=3002/tcp

# Portainer (Port 9000)
sudo firewall-cmd --permanent --zone=public --add-port=9000/tcp

# Nginx Proxy Manager (Ports 80, 81, 443)
sudo firewall-cmd --permanent --zone=public --add-port=80/tcp
sudo firewall-cmd --permanent --zone=public --add-port=81/tcp
sudo firewall-cmd --permanent --zone=public --add-port=443/tcp

# Pi-hole (Ports 53, 67, 80-alt)
sudo firewall-cmd --permanent --zone=public --add-port=53/tcp
sudo firewall-cmd --permanent --zone=public --add-port=53/udp
sudo firewall-cmd --permanent --zone=public --add-port=67/udp
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp

# Grafana (Port 3000-alt)
sudo firewall-cmd --permanent --zone=public --add-port=3001/tcp

# Prometheus (Port 9090)
sudo firewall-cmd --permanent --zone=public --add-port=9090/tcp

# GitLab (Ports 8081, 2424)
sudo firewall-cmd --permanent --zone=public --add-port=8081/tcp
sudo firewall-cmd --permanent --zone=public --add-port=2424/tcp

# SSH (should already be open)
sudo firewall-cmd --permanent --zone=public --add-service=ssh

# Apply all firewall changes
sudo firewall-cmd --reload

Verify Firewall Rules

# Check active zones
sudo firewall-cmd --get-active-zones

# List all rules for public zone
sudo firewall-cmd --zone=public --list-all

# Check if Docker integration is working
sudo firewall-cmd --zone=trusted --list-all

Directory Structure Setup

Create Main Data Directory

# Create the main /data directory
sudo mkdir -p /data

# Set ownership
sudo chown $USER:$USER /data

# Set permissions
sudo chmod 755 /data

Create Service Directories

# Create directories for each service
mkdir -p /data/homepage/config
mkdir -p /data/openspeedtest
mkdir -p /data/portainer
mkdir -p /data/nginx-proxy-manager/data
mkdir -p /data/nginx-proxy-manager/letsencrypt
mkdir -p /data/pihole/config
mkdir -p /data/pihole/dnsmasq
mkdir -p /data/grafana/data
mkdir -p /data/prometheus/config
mkdir -p /data/prometheus/data
mkdir -p /data/gitlab/config
mkdir -p /data/gitlab/logs
mkdir -p /data/gitlab/data

# Create shared directories
mkdir -p /data/logs
mkdir -p /data/backups
mkdir -p /data/compose

Set Directory Permissions

# Set proper ownership for specific services
sudo chown -R 472:472 /data/grafana/data  # Grafana user
sudo chown -R 65534:65534 /data/prometheus/data  # Prometheus user
sudo chown -R 1000:1000 /data/pihole  # Pi-hole default user

# Ensure main user can access all directories
sudo chown -R $USER:$USER /data/homepage
sudo chown -R $USER:$USER /data/openspeedtest
sudo chown -R $USER:$USER /data/portainer
sudo chown -R $USER:$USER /data/nginx-proxy-manager
sudo chown -R $USER:$USER /data/gitlab

Docker Compose Configuration

Create Main Docker Compose File

# Navigate to compose directory
cd /data/compose

# Create the main docker-compose.yml file
nano docker-compose.yml

Complete Docker Compose Configuration

version: '3.8'

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

volumes:
  portainer_data:
  prometheus_data:
  grafana_data:
  gitlab_data:
  gitlab_logs:
  gitlab_config:

services:
  # Homepage - Dashboard
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - /data/homepage/config:/app/config
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - PUID=1000
      - PGID=1000
      - HOMEPAGE_ALLOWED_HOSTS=localhost:3000,10.100.30.104:3000
    networks:
      - frontend

  # OpenSpeedTest - Network Speed Testing
  openspeedtest:
    image: openspeedtest/latest
    container_name: openspeedtest
    restart: unless-stopped
    ports:
      - "3001:3000"  # HTTP
      - "3002:3001"  # HTTPS
    # For custom data volume (optional), comment out if not needed:
    # volumes:
      # - /data/openspeedtest:/var/log/nginx
    networks:
      - frontend

  # Portainer - Docker Management
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    ports:
      - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    networks:
      - frontend

  # Nginx Proxy Manager - Reverse Proxy
  nginx-proxy-manager:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - "80:80"
      - "81:81"
      - "443:443"
    volumes:
      - /data/nginx-proxy-manager/data:/data
      - /data/nginx-proxy-manager/letsencrypt:/etc/letsencrypt
    environment:
      - DB_SQLITE_FILE=/data/database.sqlite
    networks:
      - frontend

  # Pi-hole - DNS Ad Blocker
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "67:67/udp"
      - "8080:80/tcp"
    volumes:
      - /data/pihole/config:/etc/pihole
      - /data/pihole/dnsmasq:/etc/dnsmasq.d
    environment:
      - TZ=America/Chicago
      - WEBPASSWORD=changeme123
      - DNS1=1.1.1.1
      - DNS2=1.0.0.1
    cap_add:
      - NET_ADMIN
    dns:
      - 127.0.0.1
      - 1.1.1.1
    networks:
      - frontend

  # Prometheus - Metrics Collection
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - /data/prometheus/config:/etc/prometheus
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--web.enable-lifecycle'
    networks:
      - backend

  # Grafana - Metrics Visualization
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3003:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=changeme123
      - GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
    networks:
      - backend
      - frontend

  # GitLab - Git Repository and CI/CD
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: unless-stopped
    hostname: 'gitlab.local'
    ports:
      - "8081:80"
      - "2424:22"
    volumes:
      - gitlab_config:/etc/gitlab
      - gitlab_logs:/var/log/gitlab
      - gitlab_data:/var/opt/gitlab
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab.local:8081'
        gitlab_rails['gitlab_shell_ssh_port'] = 2424
        gitlab_rails['initial_root_password'] = 'changeme123'
    shm_size: '256m'
    networks:
      - frontend

Service Configuration

Prometheus Configuration

# Create Prometheus configuration
cat > /data/prometheus/config/prometheus.yml << 'EOF'
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node'
    static_configs:
      - targets: ['host.docker.internal:9100']

  - job_name: 'docker'
    static_configs:
      - targets: ['host.docker.internal:9323']
EOF

Homepage Configuration

# Create basic Homepage configuration
cat > /data/homepage/config/settings.yaml << 'EOF'
title: Home Dashboard
headerStyle: clean
statusStyle: dot
layout:
  - Services:
      style: row
      columns: 4
      
providers:
  docker:
    endpoint: unix:///var/run/docker.sock
EOF

# Create services configuration
cat > /data/homepage/config/services.yaml << 'EOF'
- Infrastructure:
    - Portainer:
        href: http://:9000
        description: Docker Management
        icon: portainer.png
        server: my-docker
        container: portainer
        
    - Pi-hole:
        href: http://:8080/admin
        description: DNS Ad Blocker
        icon: pi-hole.png
        server: my-docker
        container: pihole
        
    - Nginx Proxy Manager:
        href: http://:81
        description: Reverse Proxy
        icon: nginx-proxy-manager.png
        server: my-docker
        container: nginx-proxy-manager

- Monitoring:
    - Grafana:
        href: http://:3003
        description: Metrics Visualization  
        icon: grafana.png
        server: my-docker
        container: grafana
        
    - Prometheus:
        href: http://:9090
        description: Metrics Collection
        icon: prometheus.png
        server: my-docker
        container: prometheus

- Development:
    - GitLab:
        href: http://:8081
        description: Git Repository
        icon: gitlab.png
        server: my-docker
        container: gitlab
        
    - Speed Test:
        href: http://:3001
        description: Network Speed Test
        icon: speedtest-tracker.png
        server: my-docker
        container: openspeedtest
EOF

# Create Docker configuration
cat > /data/homepage/config/docker.yaml << 'EOF'
my-docker:
  host: unix:///var/run/docker.sock
EOF

Deployment

Deploy Services

# Navigate to compose directory
cd /data/compose

# Deploy all services
docker compose up -d

# Check deployment status
docker compose ps

# View logs for any issues
docker compose logs -f

Verify Services

# Check all containers are running
docker ps

# Check specific service logs
docker compose logs homepage
docker compose logs pihole
docker compose logs grafana

Post-Deployment Tasks

Configure Individual Services

Pi-hole Setup

# Access Pi-hole admin interface at http://YOUR_IP:8080/admin
# Default password: changeme123
# Change the password:
docker exec -it pihole pihole -a -p

Nginx Proxy Manager Setup

# Access at http://YOUR_IP:81
# Default credentials:
# Email: admin@example.com
# Password: changeme

Grafana Setup

# Access at http://YOUR_IP:3003
# Default credentials:
# Username: admin
# Password: changeme123

# Add Prometheus data source: http://prometheus:9090

GitLab Setup

# GitLab will take several minutes to initialize
# Access at http://YOUR_IP:8081
# Username: root
# Password: changeme123

Security Hardening

# Change all default passwords immediately after deployment
# Update firewall rules to restrict access as needed
# Consider using Nginx Proxy Manager for SSL termination
# Regularly update container images

# Example: Update all containers
cd /data/compose
docker compose pull
docker compose up -d

Troubleshooting

Common Issues and Solutions

Container Won’t Start

# Check logs
docker compose logs SERVICE_NAME

# Check resource usage
docker stats

# Restart specific service
docker compose restart SERVICE_NAME

Port Conflicts

# Check what's using a port
sudo netstat -tulpn | grep :PORT

# Stop conflicting service
sudo systemctl stop SERVICE_NAME

Permission Issues

# Fix data directory permissions
sudo chown -R $USER:$USER /data/SERVICE_NAME

# Fix Docker socket permissions
sudo chmod 666 /var/run/docker.sock

Firewall Issues

# Check if ports are open
sudo firewall-cmd --zone=public --list-ports

# Add missing port
sudo firewall-cmd --permanent --zone=public --add-port=PORT/tcp
sudo firewall-cmd --reload

Docker Network Issues

# Restart Docker service
sudo systemctl restart docker

# Recreate networks
docker compose down
docker compose up -d

Resource Monitoring

# Monitor system resources
htop

# Monitor Docker resources
docker stats

# Check disk usage
df -h
du -sh /data/*

Tips and Reminders

Regular Maintenance

Performance Optimization

Security Best Practices

Backup Strategy

# Create backup script
cat > /data/backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/data/backups/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

# Backup configurations
tar -czf $BACKUP_DIR/configs.tar.gz /data/*/config/
tar -czf $BACKUP_DIR/compose.tar.gz /data/compose/

# Backup Docker volumes
docker compose -f /data/compose/docker-compose.yml stop
tar -czf $BACKUP_DIR/volumes.tar.gz /var/lib/docker/volumes/
docker compose -f /data/compose/docker-compose.yml start

echo "Backup completed: $BACKUP_DIR"
EOF

chmod +x /data/backup.sh

Useful Commands

# Quick service restart
docker compose restart SERVICE_NAME

# View all logs
docker compose logs -f

# Update specific service
docker compose pull SERVICE_NAME
docker compose up -d SERVICE_NAME

# Clean up unused resources
docker system prune -a

# Monitor resources
watch docker stats

This completes your Rocky Linux 9 Docker deployment guide. All services should now be accessible and functional. Remember to change default passwords and implement proper security measures before using in production environments.

Complete WSL Guide for Windows 11

Created using Perplexity AI

Windows Subsystem for Linux (WSL) Guide for Windows 11

Inspired by NetworkChuck’s practical approach, adapted for any Linux distro!


1. Prerequisites


2. Install WSL - Windows Subsystem for Linux

Open Windows Terminal as Administrator and run:

powershell
wsl --install

Reboot your PC when prompted.


3. Install a Different Linux Distribution

To see available Linux versions:

powershell
wsl --list --online

Example: Install Fedora Linux

powershell
wsl --install -d FedoraLinux-42

Alternative: Via Microsoft Store


4. Initial Setup


5. Keeping Your WSL Distributions Up-to-Date

Update the Linux Kernel (if prompted):

powershell
wsl --update

Update Linux Packages (inside your distro):


6. Basic WSL Management


7. Troubleshooting: Resetting the Root Password

If you forget/lost your root password for a WSL distro:

a. Launch as Root (no password needed):

powershell
wsl -d <distro-name> -u root

b. Reset Your User Password

passwd username

(Enter and confirm your new password.)

List all users if needed:

cat /etc/passwd

c. Exit and Relaunch Normally


8. Extra Tips


Bonus: GUI Integration and Docker Support

A. GUI Integration with WSLg

Windows 11 (and updated Windows 10) includes WSLg (Windows Subsystem for Linux GUI), so you can run Linux graphical apps natively.

How to Use

To install more GUI apps:


B. Docker Support on WSL2

  1. Install Docker Desktop (from the official Docker website).
  2. During setup, select “Use WSL2 instead of Hyper-V”.
  3. Launch Docker Desktop, verify your WSL2 distros are listed in
    Settings > Resources > WSL Integration.
  4. Enable integration for your distro.
  5. From your WSL terminal, test Docker:
    docker --version
    docker run hello-world
    

Enjoy combining the best of Linux and Windows!

Video Preview: Linux on Windows……Windows on Linux

Linux on Windows......Windows on Linux

Watch here: Linux on Windows……Windows on Linux

Short Description:
This video by NetworkChuck provides an in-depth, practical guide to using WSL 2 on Windows 11. It covers installation, setup, command and GUI interoperability, Docker usage, file integration, and advanced troubleshooting—perfect for anyone wanting to combine the power of Linux with the convenience of Windows!

XCA Home Lab PKI Guide

Created using Perplexity AI

Guide to Using XCA for Home Lab PKI: Root CA, Intermediate CA, and Certificates

Using XCA to Issue Root CA, Intermediate CA, and Certificates

1. Setting Up XCA and Creating a New Database

2. Creating a Root CA with Templates

3. Creating an Intermediate CA

4. Creating Certificates with Key Usages using Templates

5. Issuing Certificates from Templates


Security Best Practices for XCA and Home Lab PKI


Importing CA Certificates on Systems

Windows

  1. Double-click the Root CA certificate file (.crt or .cer).
  2. Click “Install Certificate.”
  3. Choose Local Machine store and run as Administrator.
  4. Navigate to Trusted Root Certification AuthoritiesCertificates.
  5. Use Import wizard, select the CA cert and import.
  6. Confirm and finish; restart browsers if needed.

Linux (Ubuntu/Debian example)

  1. Copy your CA certificate (.crt) to /usr/local/share/ca-certificates/:
    sudo cp your-ca.crt /usr/local/share/ca-certificates/
    
  2. Update CA certificates:
    sudo update-ca-certificates
    
  3. For other distros like RHEL/CentOS or Fedora, use the equivalent CA cert directory and trust update commands.

This guide equips users to establish a private PKI with XCA for home lab use, including creating root and intermediate CAs, issuing certificates with appropriate key usages, applying security best practices, and deploying CA certificates on client systems.

Guide to Setting Up a Free Personal Website

Created using Perplixity AI

Guide to Setting Up Custom Domain on Cloudflare Pages with GitHub Pages

1. Account Creation

2. Prepare Your Website on GitHub Pages (Updated)

3. Connect Cloudflare Pages to GitHub

4. Add a Custom Domain on Cloudflare Pages

5. DNS Setup If Domain Is Not Hosted on Cloudflare

6. Verify and Activate


This setup ensures automated deployment from GitHub with Cloudflare delivering content securely and efficiently.

Why DNS Issues Are Often Mistaken for Network Issues

Created using Perplixity AI

Watch “What is DNS? (and how it makes the Internet work)” on YouTube

This video by NetworkChuck explains how DNS (Domain Name System) serves as the crucial translator between the web addresses we type and the IP addresses computers use—illustrating how DNS failures can break internet and application connectivity in ways that mimic physical network problems.


Why DNS Issues Are Often Mistaken for Network Issues

DNS (Domain Name System) is a foundational component of all internet and internal network communication. When DNS fails, it can appear as if the entire network is down—even when physical connectivity is perfectly fine. This brief explains the impact on both client-server and server-server communication and why even experienced System Engineers often mistake DNS failures for network problems.


Impact on Client-Server and Server-Server Communication


Why DNS Failures Mimic Physical Network Outages


Example Scenario

A system engineer investigating why servers cannot communicate may:

  1. Observe all connections failing.
  2. Assume the switch, router, or firewall is at fault.
  3. Overlook that only name-based connections fail, while connections using direct IPs work.
  4. Spend hours checking physical and link-layer connectivity, before realizing the root cause is DNS (e.g., a misconfiguration, failed server, or expired record).

Key Takeaway

Because DNS underpins the very ability for computers to find each other, its failure breaks critical network functions—masking as a total network outage. Accurate troubleshooting requires checking both DNS and underlying connectivity, even for seasoned professionals.

Tech Tip: Fixing 421 Misdirected Request SNI Issues Between HAProxy and Apache

Created using Perplixity AI

Tech Tip: Fixing 421 Misdirected Request SNI Issues Between HAProxy and Apache

Overview

The HTTP 421 Misdirected Request error occurs when Apache receives an HTTPS request with an SNI hostname that doesn’t match its configured virtual hosts. This often happens when HAProxy is used as a reverse proxy in front of Apache and does not properly forward or handle the Server Name Indication (SNI) during TLS negotiation.


Why It Happens


How to Fix It

HAProxy Configuration (SSL Passthrough)

frontend https-in
    bind *:443 ssl crt /etc/ssl/certs/haproxy.pem
    mode tcp
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }
    default_backend apache-https-backend

backend apache-https-backend
    mode tcp
    server apache1 192.168.0.2:443 send-proxy-v2 ssl verify none sni str(1,32)

Key points:

HAProxy Configuration (SSL Termination)

frontend https-in
    bind *:443 ssl crt /etc/ssl/certs/haproxy.pem
    mode http
    default_backend apache-backend

backend apache-backend
    mode http
    server apache1 192.168.0.2:80 check

Notes:


Apache Virtual Host Example

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/example.crt
    SSLCertificateKeyFile /etc/ssl/private/example.key
</VirtualHost>

Testing & Validation

Use curl to test and confirm no 421 errors:

curl -IkH "Host: example.com" https://haproxy-ip

Summary

This tip helps avoid 421 Misdirected Request errors in modern HAProxy-Apache reverse proxy TLS setups.

Best YouTube Tutorials: Learning Jekyll with GitHub

Created using Perplixity AI

▶️YouTube search: Learn Jekyll

If you’re looking to learn Jekyll and deploy static sites using GitHub Pages, these recent and popular YouTube videos offer step-by-step guidance for beginners and intermediate users.


1. Create an Online Resume Using GitHub Pages and Jekyll: Step-by-Step Guide

Published: May 2025 (41 min)
This comprehensive tutorial walks you through creating an online resume on GitHub Pages with Jekyll. Covers forking a template, editing with GitHub, and using GitHub Copilot for troubleshooting and personalization.

Create an Online Resume Using GitHub Pages and Jekyll


2. Learn Jekyll with GitHub Pages (Playlist)

Published: April 2025 (Multiple Episodes)
A playlist covering the essentials: starting your blog, themes, layouts, and advanced options for GitHub Pages hosting with Jekyll.

Learn Jekyll with GitHub Pages Note: For playlists, the thumbnail shows the first video.


3. Minimal Mistakes Jekyll Theme Tutorial

Published: Oct 2024 (1 hr 3 min)
Deep dive into the Minimal Mistakes theme with custom layouts, navigation, and other advanced features. Perfect for creating professional, polished websites.

Minimal Mistakes Jekyll Theme Tutorial


4. Jekyll Static Site Generator & GitHub Pages Quickstart

Published: Oct 2024 (48 min)
A practical walkthrough from install to deployment: craft a local Jekyll site, push to GitHub, work with themes (Minima, Hacker), and automate with GitHub Actions. Great for first-timers!

Jekyll Static Site Generator & GitHub Pages Quickstart


5. How to Build a Website | Github Pages | Jekyll | Template

Published: Feb 2022 (11 min)
A beginner-friendly video for launching your personal

Jekyll Front Matter Guide

Created using Perplixity AI

Jekyll Front Matter Guide

Jekyll uses front matter, a block of YAML options placed at the top of your file, to add metadata and configure processing. This guide covers how to format front matter and all common configuration options for Jekyll posts and pages.


What is Front Matter?

Front matter is a YAML block at the very top of your file, surrounded by triple dashes (---). Jekyll reads this block to process the file—assigning templates, defining variables, or specifying metadata.

Example basic front matter:

---
layout: post
title: "Blogging Like a Hacker"
date: 2025-08-26
categories: jekyll guide
tags: [jekyll, blog, static-site]
author: "Jane Doe"
published: true
excerpt: "A quick intro to writing blog posts with Jekyll."
permalink: /blogging/introduction/
---

Anything below the front matter block is normal Markdown, HTML, or Liquid.


Required Syntax


Common Front Matter Variables

Variable Applies to Description
layout post/page Template file to use from _layouts (default, post, page, etc.)
title post/page Title for the content
date post/page Publish/sort date. Format: YYYY-MM-DD HH:mm:ss +/-ZZ:ZZ (time optional)
categories post/page List or string; assign categories for grouping/posts
tags post/page List or string; assign tags for filtering/metadata
author post/page Author attribution
published post/page true/false; mark as published/unpublished
excerpt post/page Short summary for lists/previews
permalink post/page Custom URL for the file/page
description post/page SEO, meta, preview, or context description
redirect_to page List or string; URLs to redirect this page to
hidden page Prevent from showing in navigation (used in some themes)
lang page Language code (e.g., en, for translation/localization support)
toc page Enable/disable automatic table of contents
showMiniToc page Enable/disable Github Docs-style mini TOC (true/false)
draft post Mark as draft (not published until manually published)
sitemap post/page Exclude from sitemap.xml (false)

Examples

Using Categories and Tags

---
categories:
  - guides
  - markdown
tags:
  - tips
  - beginners
---

You may also use space-separated strings:

---
categories: guides markdown
tags: tips beginners
---

Multiline Variables

For long descriptions or excerpts, you can use YAML’s multiline format.

Folded style (line breaks become spaces):

---
description: >
  This page demonstrates advanced usage of Jekyll front matter.
  You can have multiple lines here.
---

Literal style (preserves newlines):

---
excerpt: |-
  This is a paragraph
  with multiple lines
  exactly as written.
---

Custom Variables

You can create any key-value you want:

---
layout: page
title: Custom Page
custom_variable: "Display this value using "
---

Special Page Types


Optional: Empty Front Matter

To enable Liquid templating on a plain page, use empty front matter:

---
---

Best Practices & Tips


Resources


You’re now ready to format front matter for any Jekyll post, page, or collection!

How to create .md files

Created using Perplexity AI

Beginner’s Guide to Creating Markdown Files

Markdown is a lightweight markup language that makes it easy to format text for web pages, documentation, and README files. This guide will teach you everything you need to know to start creating your own .md files.

What is Markdown?

Markdown is a simple way to add formatting to plain text documents. It uses special characters and symbols to create headings, lists, links, and other formatting elements. The best part? It’s designed to be readable even in its raw form.

Creating Your First Markdown File

  1. Open a text editor (VS Code, Notepad++, Sublime Text, or even basic Notepad)
  2. Create a new file and save it with a .md extension
    • Example: README.md, notes.md, guide.md
  3. Start writing using Markdown syntax

Basic Markdown Syntax

Headings

Use # symbols to create headings. More # symbols = smaller heading:

# Heading 1 (Largest)
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6 (Smallest)

Text Formatting

**Bold text**
*Italic text*
***Bold and italic***
~~Strikethrough~~

Lists

Unordered Lists:

- First item
- Second item
- Third item
  - Sub-item
  - Another sub-item

Ordered Lists:

1. First item
2. Second item
3. Third item
   1. Sub-item
   2. Another sub-item

Links:

[Link text](https://www.example.com)
[GitHub](https://github.com)

Images:

![Alt text](image-url.jpg)
![GitHub Logo](https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png)

Code

Inline code:

Use `backticks` for inline code.

Code blocks:

```
This is a code block
You can write multiple lines here
```

Code blocks with syntax highlighting:

```python
def hello_world():
    print("Hello, World!")
```

Blockquotes

> This is a blockquote
> It can span multiple lines
> 
> And even include multiple paragraphs

Tables

| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Row 1    | Data     | Data     |
| Row 2    | Data     | Data     |

Horizontal Rules

Create horizontal lines with three or more dashes:

---

Advanced Elements

Task Lists

- [x] Completed task
- [ ] Incomplete task
- [ ] Another task

Line Breaks

Escaping Characters

Use backslash \ to escape special characters:

\*This won't be italic\*
\# This won't be a heading

Common File Types

Example README.md

Here’s a sample README file structure:

# Project Title

Brief description of your project.

## Features

- Feature 1
- Feature 2
- Feature 3

## Installation

```bash
git clone https://github.com/username/project.git
cd project
npm install

Usage

Explain how to use your project here.

Contributing

Pull requests are welcome. For major changes, please open an issue first.

License

MIT ```

Tips for Better Markdown

  1. Keep it simple - Markdown is meant to be readable
  2. Use consistent formatting - Pick a style and stick with it
  3. Preview your work - Many editors show live previews
  4. Learn as you go - Start with basics and add complexity over time

Where Markdown is Used

Resources


Happy writing! 🎉 Start with the basics and gradually incorporate more advanced features as you become comfortable with Markdown.