Compare commits
32
Commits
optimize-media
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40c58e8d9b | ||
|
|
00ba36191f | ||
|
|
2a18854894 | ||
|
|
c157c599ea | ||
|
|
810069d1fe | ||
|
|
e2a530045d | ||
|
|
9e69de9fd8 | ||
|
|
808187bef2 | ||
|
|
ae74124319 | ||
|
|
eefcee2ce3 | ||
|
|
d9cf96e594 | ||
|
|
b809a14e2b | ||
|
|
a0878cf30e | ||
|
|
f28aca8484 | ||
|
|
84495ae064 | ||
|
|
e8d04b8373 | ||
|
|
a4b9e5a040 | ||
|
|
9fc7c371c5 | ||
|
|
1ef8f861c1 | ||
|
|
3c5678f8a8 | ||
|
|
319c8fa922 | ||
|
|
b84843ca3d | ||
|
|
4b0b681302 | ||
|
|
2c6f20fbc3 | ||
|
|
b88828f92e | ||
|
|
09780d9598 | ||
|
|
c24ed01e0a | ||
|
|
7e1728a037 | ||
|
|
30ef38b143 | ||
|
|
f927bc191c | ||
|
|
f402556380 | ||
|
|
25b6e0575a |
@@ -0,0 +1,18 @@
|
|||||||
|
# Send only files required by the Hugo build and SWS runtime stages.
|
||||||
|
*
|
||||||
|
|
||||||
|
!Dockerfile
|
||||||
|
!go.mod
|
||||||
|
!go.sum
|
||||||
|
!sws.toml
|
||||||
|
|
||||||
|
!assets/
|
||||||
|
!assets/**
|
||||||
|
!config/
|
||||||
|
!config/**
|
||||||
|
!content/
|
||||||
|
!content/**
|
||||||
|
!layouts/
|
||||||
|
!layouts/**
|
||||||
|
!static/
|
||||||
|
!static/**
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
name: Build Hugo
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: tea.ahmza.com
|
||||||
|
IMAGE_NAME: ${{ gitea.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Login to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ gitea.repository_owner }}
|
||||||
|
password: ${{ secrets.GITEATOKEN }}
|
||||||
|
|
||||||
|
# QEMU deliberately omitted.
|
||||||
|
#
|
||||||
|
# Hugo runs on BUILDPLATFORM in the Dockerfile, so the static site is
|
||||||
|
# built natively on the runner. The final amd64/arm64 stages only copy
|
||||||
|
# the resulting static files into the target-platform SWS image.
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/arm64, linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:latest
|
||||||
|
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:${{ gitea.event.head_commit.id }}
|
||||||
|
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache
|
||||||
|
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache,mode=max
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Pre-flight - verify VPN prerequisites
|
||||||
|
# NetBird brings up a WireGuard interface, which needs a TUN device and
|
||||||
|
# NET_ADMIN inside the job container. Without them, `netbird up` hangs
|
||||||
|
# ~50s and dies with a cryptic "DeadlineExceeded". Check up front so the
|
||||||
|
# failure is obvious and points at the fix (runner config.yaml).
|
||||||
|
run: |
|
||||||
|
echo "::group::NetBird prerequisites"
|
||||||
|
if [ -e /dev/net/tun ]; then
|
||||||
|
echo "OK: /dev/net/tun present"
|
||||||
|
else
|
||||||
|
echo "::error title=Missing TUN device::/dev/net/tun not found. Add --device=/dev/net/tun to the runner's config.yaml container.options."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if command -v capsh >/dev/null 2>&1; then
|
||||||
|
capsh --print | grep -q 'cap_net_admin' \
|
||||||
|
&& echo "OK: NET_ADMIN present" \
|
||||||
|
|| echo "::warning title=NET_ADMIN missing::netbird up will likely time out (DeadlineExceeded) configuring wt0. Add --cap-add=NET_ADMIN to the runner's config.yaml container.options."
|
||||||
|
else
|
||||||
|
echo "note: capsh unavailable, skipping NET_ADMIN check"
|
||||||
|
fi
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
- name: Netbird Connect
|
||||||
|
id: netbird
|
||||||
|
timeout-minutes: 3
|
||||||
|
uses: Alemiz112/netbird-connect@v1
|
||||||
|
with:
|
||||||
|
setup-key: ${{ secrets.NETBIRD_SETUP_KEY }}
|
||||||
|
hostname: 'gitea-actions-blog'
|
||||||
|
management-url: ${{ secrets.NETBIRD_URL }}
|
||||||
|
|
||||||
|
- name: NetBird status
|
||||||
|
# Surface the actual peer/route state in the log and hard-assert that
|
||||||
|
# the management connection is up before attempting any deploys.
|
||||||
|
run: |
|
||||||
|
echo "::group::netbird status"
|
||||||
|
sudo netbird status --detail || true
|
||||||
|
echo "::endgroup::"
|
||||||
|
sudo netbird status | grep -q 'Management: Connected' \
|
||||||
|
&& echo "NetBird management connected." \
|
||||||
|
|| { echo "::error title=NetBird not connected::Management not connected after netbird up."; exit 1; }
|
||||||
|
|
||||||
|
- name: Deploy image to talos-prod
|
||||||
|
uses: actions-hub/kubectl@v1.36.2
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||||
|
with:
|
||||||
|
args: >-
|
||||||
|
-n blog-ahmza-com
|
||||||
|
set image deployment/blog
|
||||||
|
blog=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:${{ gitea.event.head_commit.id }}
|
||||||
|
|
||||||
|
- name: Wait for rollout
|
||||||
|
uses: actions-hub/kubectl@v1.36.2
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||||
|
with:
|
||||||
|
args: >-
|
||||||
|
-n blog-ahmza-com
|
||||||
|
rollout status deployment/blog
|
||||||
|
--timeout=5m
|
||||||
|
|
||||||
|
- name: Show deployed pods
|
||||||
|
uses: actions-hub/kubectl@v1.36.2
|
||||||
|
env:
|
||||||
|
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||||
|
with:
|
||||||
|
args: >-
|
||||||
|
-n blog-ahmza-com
|
||||||
|
get pods -o wide
|
||||||
|
|
||||||
|
- name: NetBird diagnostics & disconnect
|
||||||
|
# Runs even on failure: captures final state for debugging and tears the
|
||||||
|
# peer down so stale 'gitea-actions-blog' peers don't pile up.
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "::group::final netbird status"
|
||||||
|
sudo netbird status --detail || echo "netbird unavailable"
|
||||||
|
echo "::endgroup::"
|
||||||
|
sudo netbird down || true
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
name: Build Hugo
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: tea.ahmza.com
|
|
||||||
IMAGE_NAME: ${{ gitea.repository }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
attestations: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Login to Gitea Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ gitea.repository_owner }}
|
|
||||||
password: ${{ secrets.GITEATOKEN }}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/arm64, linux/amd64
|
|
||||||
push: true
|
|
||||||
build-args: |
|
|
||||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=true
|
|
||||||
tags: |
|
|
||||||
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:latest
|
|
||||||
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:${{ gitea.event.head_commit.id }}
|
|
||||||
# Registry-backed build cache. The GitHub-hosted 'gha' cache is not
|
|
||||||
# reachable from self-hosted Gitea runners, so store the cache as a
|
|
||||||
# dedicated tag in the same registry instead.
|
|
||||||
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache
|
|
||||||
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache,mode=max
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Netbird Connect
|
|
||||||
uses: Alemiz112/netbird-connect@v1
|
|
||||||
with:
|
|
||||||
setup-key: ${{ secrets.NETBIRD_SETUP_KEY }}
|
|
||||||
hostname: 'gitea-actions-blog'
|
|
||||||
management-url: ${{ secrets.NETBIRD_URL }}
|
|
||||||
|
|
||||||
- name: Deploy to RPIC-Cluster
|
|
||||||
# RPIC cluster is temporarily out of service. Don't let its failure
|
|
||||||
# block the Gimli deploy or fail the whole job. Remove this line once
|
|
||||||
# the cluster is back.
|
|
||||||
continue-on-error: true
|
|
||||||
uses: appleboy/ssh-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.RPIC_HOST }}
|
|
||||||
username: ${{ secrets.SSH_USER }}
|
|
||||||
key: ${{ secrets.RPIC_KEY }}
|
|
||||||
port: ${{ secrets.SSH_PORT }}
|
|
||||||
script: 'kubectl rollout restart deployment blog -n blog-ahmza-com'
|
|
||||||
|
|
||||||
- name: Deploy to Gimli
|
|
||||||
uses: appleboy/ssh-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.GIMLI_HOST }}
|
|
||||||
username: ${{ secrets.SSH_USER }}
|
|
||||||
key: ${{ secrets.GIMLI_KEY }}
|
|
||||||
port: ${{ secrets.SSH_PORT }}
|
|
||||||
script: 'docker compose down && docker compose pull && docker compose up -d'
|
|
||||||
@@ -5,5 +5,4 @@ styles/
|
|||||||
node_modules
|
node_modules
|
||||||
public/
|
public/
|
||||||
resources/
|
resources/
|
||||||
go.sum
|
|
||||||
themes/
|
themes/
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Caddyfile for the blog behind Cloudflare.
|
||||||
|
#
|
||||||
|
# Topology: Cloudflare (terminates TLS) -> Caddy (HTTP :80) -> SWS (blog:80)
|
||||||
|
#
|
||||||
|
# Cloudflare handles the public HTTPS cert, so Caddy serves plain HTTP on :80
|
||||||
|
# and does NOT need the Cloudflare DNS-01 / automatic-HTTPS setup. Set the
|
||||||
|
# Cloudflare SSL/TLS mode to "Full" so CF->origin is encrypted at the network
|
||||||
|
# layer (e.g. over your tunnel/Netbird), even though this listener is HTTP.
|
||||||
|
#
|
||||||
|
# Copy this file to /etc/caddy/Caddyfile on the caddy_cf host and reload:
|
||||||
|
# docker exec caddy_cf caddy reload --config /etc/caddy/Caddyfile
|
||||||
|
# or restart the container.
|
||||||
|
{
|
||||||
|
# Trust only Cloudflare's edge IPs so real visitor IPs (Cf-Connecting-Ip /
|
||||||
|
# X-Forwarded-For) are honoured only when the request actually comes from
|
||||||
|
# Cloudflare. This prevents header spoofing from any other source.
|
||||||
|
#
|
||||||
|
# The `cloudflare` IP source (WeidiDeng/caddy-cloudflare-ip, baked into the
|
||||||
|
# caddy-cf image) auto-fetches the ranges from cloudflare.com/ips-v4 and
|
||||||
|
# /ips-v6 and refreshes them periodically, so there's no static list to
|
||||||
|
# keep in sync.
|
||||||
|
servers {
|
||||||
|
trusted_proxies cloudflare {
|
||||||
|
interval 12h
|
||||||
|
timeout 15s
|
||||||
|
}
|
||||||
|
# Read the real visitor IP from these headers (only honoured for
|
||||||
|
# requests from trusted_proxies above). Cf-Connecting-Ip is
|
||||||
|
# Cloudflare's single authoritative end-user IP; X-Forwarded-For is
|
||||||
|
# the fallback if a request ever arrives without the CF header.
|
||||||
|
client_ip_headers Cf-Connecting-Ip X-Forwarded-For
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bind to the hostname on the HTTP port only. Because the address has an
|
||||||
|
# explicit ":80" scheme/port, Caddy will NOT try to provision a TLS cert.
|
||||||
|
http://blog.ahmza.com {
|
||||||
|
# THE responder: proxy every request to the blog (SWS) container.
|
||||||
|
# This is what was missing before (requests fell through to a NOP -> blank page).
|
||||||
|
reverse_proxy blog:80
|
||||||
|
|
||||||
|
# Compression happens here (SWS has it disabled on purpose).
|
||||||
|
encode zstd gzip
|
||||||
|
|
||||||
|
# Security headers (single source of truth; SWS has security-headers off).
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
X-Frame-Options "SAMEORIGIN"
|
||||||
|
Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
|
# Drop Caddy's default Server header.
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
|
||||||
|
log {
|
||||||
|
output stdout
|
||||||
|
format console
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-2
@@ -1,6 +1,19 @@
|
|||||||
FROM ghcr.io/hugomods/hugo:0.157.0 AS build
|
# syntax=docker/dockerfile:1
|
||||||
|
# Pin the build stage to the native build platform so Hugo runs once (never
|
||||||
|
# under QEMU emulation). Hugo output is static HTML — identical across arches —
|
||||||
|
# so there's no reason to run it per target arch. The runtime stage below stays
|
||||||
|
# on TARGETPLATFORM, so multi-arch (amd64 + arm64) runtime images are preserved.
|
||||||
|
FROM --platform=$BUILDPLATFORM ghcr.io/hugomods/hugo:0.165.0 AS build
|
||||||
WORKDIR /tmp/hugo
|
WORKDIR /tmp/hugo
|
||||||
COPY . .
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
COPY config ./config
|
||||||
|
RUN hugo mod graph
|
||||||
|
|
||||||
|
COPY assets ./assets
|
||||||
|
COPY content ./content
|
||||||
|
COPY layouts ./layouts
|
||||||
|
COPY static ./static
|
||||||
RUN hugo --gc --minify
|
RUN hugo --gc --minify
|
||||||
|
|
||||||
FROM ghcr.io/static-web-server/static-web-server:2
|
FROM ghcr.io/static-web-server/static-web-server:2
|
||||||
|
|||||||
@@ -50,5 +50,5 @@ sacrificing performance.
|
|||||||
The Blowfish theme is managed as a Hugo module, so no submodules are required.
|
The Blowfish theme is managed as a Hugo module, so no submodules are required.
|
||||||
Clone the repository with:
|
Clone the repository with:
|
||||||
```bash
|
```bash
|
||||||
git clone ssh://gitea@tea.ahmza.com:2222/ahmza/blog-ahmza-com.git
|
git clone ssh://git@tea.ahmza.com/ahmza/blog-ahmza-com.git
|
||||||
```
|
```
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: { { replace .TranslationBaseName "-" " " | title } }
|
title: {{ replace .TranslationBaseName "-" " " | title }}
|
||||||
description: ""
|
description: ""
|
||||||
tags: [""]
|
tags: []
|
||||||
author: "ahmza"
|
author: "ahmza"
|
||||||
date: { { .Date } }
|
date: {{ .Date }}
|
||||||
draft: true
|
draft: true
|
||||||
weight:
|
weight:
|
||||||
featureimage:
|
featureimage:
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ pagination.pagerSize = 10
|
|||||||
buildDrafts = false
|
buildDrafts = false
|
||||||
buildFuture = false
|
buildFuture = false
|
||||||
|
|
||||||
|
# Keep source-art and editor backup files out of the built site. Without this,
|
||||||
|
# files inside page bundles (e.g. feature.aseprite) are copied into public/ as
|
||||||
|
# page resources and served publicly.
|
||||||
|
ignoreFiles = ['\.aseprite$', '\.bak$']
|
||||||
|
|
||||||
enableEmoji = true
|
enableEmoji = true
|
||||||
|
|
||||||
# googleAnalytics = "G-XXXXXXXXX"
|
# googleAnalytics = "G-XXXXXXXXX"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
languageCode = "en"
|
locale = "en"
|
||||||
languageName = "English"
|
label = "English"
|
||||||
weight = 1
|
weight = 1
|
||||||
title = "ahmza's ramblings"
|
title = "ahmza's ramblings"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[[imports]]
|
[[imports]]
|
||||||
disable = false
|
disable = false
|
||||||
path = "github.com/nunocoracao/blowfish/v2"
|
path = "github.com/nunocoracao/blowfish/v3"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Arrstack
|
title: Arrstack
|
||||||
description:
|
description:
|
||||||
tags: [""]
|
tags: []
|
||||||
author: "ahmza"
|
author: "ahmza"
|
||||||
date: 2025-05-09T23:44:27+01:00
|
date: 2025-05-09T23:44:27+01:00
|
||||||
draft: true
|
draft: true
|
||||||
@@ -4,7 +4,7 @@ description: In the modern age we are overwhelmed with stimuli. Learn to cut out
|
|||||||
tags:
|
tags:
|
||||||
- lifestyle
|
- lifestyle
|
||||||
- minimalism
|
- minimalism
|
||||||
- prodcutivity
|
- productivity
|
||||||
author: ahmza
|
author: ahmza
|
||||||
date: 2024-01-02
|
date: 2024-01-02
|
||||||
draft: false
|
draft: false
|
||||||
|
|||||||
@@ -185,8 +185,8 @@ cd .server-data
|
|||||||
2. **Download a Minecraft server jar file:**
|
2. **Download a Minecraft server jar file:**
|
||||||
|
|
||||||
- [Vanilla Minecraft Server](https://www.minecraft.net/en-us/download/server)
|
- [Vanilla Minecraft Server](https://www.minecraft.net/en-us/download/server)
|
||||||
- Right-click the green link that says minecraft_server.<version>.jar and copy the link address.
|
- Right-click the green link that says `minecraft_server.<version>.jar` and copy the link address.
|
||||||
- Download with: curl -o server.jar <link you copied here>
|
- Download with: `curl -o server.jar <link you copied here>`
|
||||||
- [Fabric Mod loader server](https://fabricmc.net/use/server/)
|
- [Fabric Mod loader server](https://fabricmc.net/use/server/)
|
||||||
- Select the Minecraft version, Fabric Loader Version, and the Installer Version of your choice. I tend to go with the latest.
|
- Select the Minecraft version, Fabric Loader Version, and the Installer Version of your choice. I tend to go with the latest.
|
||||||
- Download with: Copy the curl command under CLI download
|
- Download with: Copy the curl command under CLI download
|
||||||
|
|||||||
@@ -1,243 +0,0 @@
|
|||||||
---
|
|
||||||
title: Minecraft Server on Docker ⛏ 🐳
|
|
||||||
date: 2022-05-24
|
|
||||||
lastmod: 2024-07-20
|
|
||||||
author: "ahmza"
|
|
||||||
draft: false
|
|
||||||
weight:
|
|
||||||
|
|
||||||
tags: ["games", "docker"]
|
|
||||||
description: "Ever wanted to build a Minecraft server in docker !! Start here"
|
|
||||||
---
|
|
||||||
|
|
||||||
## **Introduction**
|
|
||||||
|
|
||||||
Minecraft is a great game that becomes even more enjoyable when played with
|
|
||||||
friends. One of the best ways to do this is by setting up a dedicated server.
|
|
||||||
This guide will help you do just that!
|
|
||||||
|
|
||||||
Setting up and running a Minecraft server does not require a lot of resources,
|
|
||||||
but based on my experience, allocating more resources significantly enhances
|
|
||||||
performance. Especially when you start to add mods and increase player count.
|
|
||||||
|
|
||||||
## **Prerequisites**
|
|
||||||
|
|
||||||
- Docker
|
|
||||||
- Docker Compose
|
|
||||||
|
|
||||||
See [Pi Cluster](/posts/pi-docker-swarm/)
|
|
||||||
for more information about my docker setup.
|
|
||||||
|
|
||||||
## **Requirements**
|
|
||||||
_Requirements Gathered from [here](https://minecraft.fandom.com/wiki/Server/Requirements/Dedicated#Unix_.28Linux.2C_BSD.2C_macOS.29)_
|
|
||||||
|
|
||||||
### **Minimum (1-5 Players)**
|
|
||||||
|
|
||||||
- Intel Pentium 4 or AMD Athlon based CPU
|
|
||||||
- 512 MB RAM
|
|
||||||
- 2 GB Storage space
|
|
||||||
|
|
||||||
### **Recommended (10-15 Players)**
|
|
||||||
|
|
||||||
- Intel Core or AMD K8 based CPU
|
|
||||||
- 3 GB RAM
|
|
||||||
- 16 GB Storage Space
|
|
||||||
|
|
||||||
### **Optimal (20+ Players)**
|
|
||||||
|
|
||||||
- Intel i5/i7 or AMD Ryzen 5/7 Based CPU
|
|
||||||
- 6 GB RAM
|
|
||||||
- 35 GB Storage Space
|
|
||||||
|
|
||||||
**My opinion**:
|
|
||||||
|
|
||||||
I personally went with the **optimal** settings as I wanted to run mods as well
|
|
||||||
as have the capacity for ~10 players. This gives the server breathing room.
|
|
||||||
|
|
||||||
However if you will be runing no mods or a few light weight mods and only a few
|
|
||||||
players then there really is no need to go higher than 3-4 GB.
|
|
||||||
|
|
||||||
## **Building the Container**
|
|
||||||
|
|
||||||
Lets get started on building the Minecraft server
|
|
||||||
|
|
||||||
1. **Create a directory for the Minecraft server files**:
|
|
||||||
```bash
|
|
||||||
# Make the folder
|
|
||||||
mkdir minecraft-server
|
|
||||||
# Enter the folder
|
|
||||||
cd minecraft-server
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Create an environment file for the Docker Container**:
|
|
||||||
- The _.env_ file allows us to specify variables used in the build process.
|
|
||||||
```bash
|
|
||||||
# Create a .env file
|
|
||||||
touch .env
|
|
||||||
# Open in a text editor
|
|
||||||
vim .env
|
|
||||||
# Enter the following variables
|
|
||||||
CONTAINER_NAME=minecraft-server
|
|
||||||
SERVER_MEM=4096 #change this to your desired value (in MB)
|
|
||||||
SERVER_JAR=<downloaded-server-file>.jar # Minecraft server jar file. Change this later
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Create the _docker-compose.yml_ file**:
|
|
||||||
```bash
|
|
||||||
# Create the file
|
|
||||||
touch docker-compose.yml`
|
|
||||||
# Open in a text editor
|
|
||||||
vim docker-compose.yml
|
|
||||||
```
|
|
||||||
- _docker-compose.yml_ is the spec of our Minecraft server.
|
|
||||||
- Build a docker image using context . and pass some ARGS.
|
|
||||||
- Attach volume .server-data, where Minecraft's files will be stored.
|
|
||||||
- Open network ports to access to the Docker container
|
|
||||||
- Give the docker container a name
|
|
||||||
- The container will always restart unless we tell it to stop.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
minecraft-server:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
SERVER_MEM: ${SERVER_MEM}
|
|
||||||
SERVER_JAR: ${SERVER_JAR}
|
|
||||||
volumes:
|
|
||||||
- .server-data:/server
|
|
||||||
ports:
|
|
||||||
- 25565:25565 # Game
|
|
||||||
- 25575:25575 # RCON
|
|
||||||
container_name: ${CONTAINER_NAME}
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
server-data:
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Create the _Dockerfile_ file**:
|
|
||||||
|
|
||||||
- requested in docker-compose.yml build stage as context .
|
|
||||||
- The _Dockerfile_ is the spec for software running inside the container
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create the file
|
|
||||||
touch Dockerfile
|
|
||||||
# Open the file in a text editor
|
|
||||||
vim Dockerfile
|
|
||||||
```
|
|
||||||
- We use the [eclipse-temurin:21](https://hub.docker.com/_/eclipse-temurin) image to provide the java JRE
|
|
||||||
- Use the required java version for the version of Minecraft you are running
|
|
||||||
- Collect the ARGs from the .env file
|
|
||||||
- Set the environment variables
|
|
||||||
- Set the working directory to /server which on our local system is .server-data
|
|
||||||
- Run the minecraft server jar with the ENV variables
|
|
||||||
|
|
||||||
```Dockerfile
|
|
||||||
FROM eclipse-temurin:21
|
|
||||||
|
|
||||||
ARG SERVER_MEM
|
|
||||||
ARG SERVER_JAR
|
|
||||||
|
|
||||||
ENV SERVER_MEM=${SERVER_MEM}
|
|
||||||
ENV SERVER_JAR=${SERVER_JAR}
|
|
||||||
|
|
||||||
WORKDIR /server
|
|
||||||
|
|
||||||
CMD echo "Minecraft Server is starting\n" && java -Xms128M -Xmx${SERVER_MEM}M -jar ${SERVER_JAR} nogui
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Setup the Server files**
|
|
||||||
|
|
||||||
We’re almost there, just a few steps to go.
|
|
||||||
|
|
||||||
When you try to start the container, you might encounter the following error:
|
|
||||||
```bash
|
|
||||||
Minecraft Server is starting
|
|
||||||
|
|
||||||
Error: Unable to access jarfile server.jar
|
|
||||||
```
|
|
||||||
This happens because we haven't downloaded the server jar file yet. Let's do
|
|
||||||
that now
|
|
||||||
|
|
||||||
1. **Create a folder in the minecraft-server folder called .server-data**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir .server-data
|
|
||||||
cd .server-data
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Download a Minecraft server jar file**:
|
|
||||||
- [Vanilla Minecraft Server](https://www.minecraft.net/en-us/download/server)
|
|
||||||
- Right click the green link that says minecraft_server.<version>.jar and copy the link address
|
|
||||||
- Download with: `curl -o server.jar <link you copied here>`
|
|
||||||
- [Fabric Mod loader server](https://fabricmc.net/use/server/)
|
|
||||||
- Select the Minecraft version, Fabric Loader Version and the Installer Version of your choice. I tend to go with the latest.
|
|
||||||
- Download with: `Copy the curl command under CLI download`
|
|
||||||
|
|
||||||
3. **Go back to the root minecraft-server folder**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Edit SERVER_JAR in .env with the name of the .jar you just downloaded**:
|
|
||||||
```bash
|
|
||||||
# Open in a text editor
|
|
||||||
vim .env
|
|
||||||
# Enter the following variables
|
|
||||||
CONTAINER_NAME=minecraft-server
|
|
||||||
SERVER_MEM=4096
|
|
||||||
SERVER_JAR=<downloaded-server-file>.jar # Change this to the name of the .jar you downloaded
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Run the container to generate necessary files like 'eula.txt'**:
|
|
||||||
```bash
|
|
||||||
docker compose up
|
|
||||||
```
|
|
||||||
you will see something like this:
|
|
||||||
```bash
|
|
||||||
minecraft-server | Starting net.minecraft.server.Main
|
|
||||||
minecraft-server | [18:05:50] [ServerMain/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
|
|
||||||
minecraft-server exited with code 0
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **Accept the EULA**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# edit eula.txt in a text editor
|
|
||||||
vim .server-data/eula.txt
|
|
||||||
# change `eula=false` to `eula=true`
|
|
||||||
eula=true
|
|
||||||
```
|
|
||||||
|
|
||||||
7. **Make any additional changes you want**:
|
|
||||||
- Notable files:
|
|
||||||
- **server.properties** - Configure server name, game mode, world type etc…
|
|
||||||
- **ops.json** - server operators (can be set in-game)
|
|
||||||
- **whitelist.json** - players allowed to join the server (can be set in-game)
|
|
||||||
|
|
||||||
## Run the Minecraft Server
|
|
||||||
|
|
||||||
1. **Run the container in detached mode**:
|
|
||||||
```bash
|
|
||||||
# The -d flag allows the container to run in detached mode, i.e., in the background
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# monitor the logs as the server starts. Use CTRL+C to exit
|
|
||||||
docker logs minecraft-server --follow
|
|
||||||
# If you see the following lines, your server is up and running:
|
|
||||||
[18:17:01] [Server thread/INFO]: Time elapsed: 25529 ms
|
|
||||||
[18:17:01] [Server thread/INFO]: Done (34.571s)! For help, type "help"
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Conclusion**
|
|
||||||
|
|
||||||
Congratulations! You've successfully set up a Minecraft server. Now it's time
|
|
||||||
to play!
|
|
||||||
|
|
||||||
There are many exciting possibilities from here, such as installing mods or
|
|
||||||
creating scripts to manage the server. Enjoy your Minecraft adventures!
|
|
||||||
@@ -39,8 +39,6 @@ a **bare metal server that sucks a lot of power to a low power solution.**
|
|||||||
- 4 x PoE hats
|
- 4 x PoE hats
|
||||||
- 1 x 5 port PoE Switch
|
- 1 x 5 port PoE Switch
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### **Point Of This post**
|
### **Point Of This post**
|
||||||
|
|
||||||
After following this post you will have setup _Docker swarm_ on a cluster of Pis
|
After following this post you will have setup _Docker swarm_ on a cluster of Pis
|
||||||
@@ -329,7 +327,7 @@ vim hosts
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **_[piCluster]_** - specifies a group of hosts
|
- **_[piCluster]_** - specifies a group of hosts
|
||||||
- **_<ip address>_** - change these to the Pis IP addresses
|
- **_`<ip address>`_** - change these to the Pis IP addresses
|
||||||
|
|
||||||
**create the Pi Intercommunication keys**
|
**create the Pi Intercommunication keys**
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
title: Securing SSH 🤖
|
title: Securing SSH 🤖
|
||||||
description: Ever wanted to make your SSH connections more secure. Read this
|
description: Ever wanted to make your SSH connections more secure. Read this
|
||||||
tags:
|
tags:
|
||||||
- HomeLab
|
- homelab
|
||||||
- RemoteAccess
|
- remoteaccess
|
||||||
- SSH
|
- ssh
|
||||||
author: ahmza
|
author: ahmza
|
||||||
date: 2021-12-07
|
date: 2021-12-07
|
||||||
draft: false
|
draft: false
|
||||||
@@ -146,7 +146,7 @@ fine 😊
|
|||||||
- Max Auth Tries 5
|
- Max Auth Tries 5
|
||||||
- PasswordAuthentication no
|
- PasswordAuthentication no
|
||||||
- PermitEmptyPasswords no
|
- PermitEmptyPasswords no
|
||||||
- AllowUsers <user>
|
- AllowUsers `<user>`
|
||||||
- ClientAliveInterval 180
|
- ClientAliveInterval 180
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ title: Setup a HUGO Site
|
|||||||
description: Always wanted to setup a static website. Look no further !! In this post we go over the basics of setting up a HUGO site
|
description: Always wanted to setup a static website. Look no further !! In this post we go over the basics of setting up a HUGO site
|
||||||
tags:
|
tags:
|
||||||
- blog
|
- blog
|
||||||
- HUGO
|
- hugo
|
||||||
- web
|
- web
|
||||||
- development
|
- development
|
||||||
author: ahmza
|
author: ahmza
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ wsl --install
|
|||||||
Go to the Microsoft store and search for a Linux distribution. I prefer
|
Go to the Microsoft store and search for a Linux distribution. I prefer
|
||||||
to use the "Ubuntu" distribution.
|
to use the "Ubuntu" distribution.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Once installed, go ahead and run the distribution, after it finishes setting
|
Once installed, go ahead and run the distribution, after it finishes setting
|
||||||
itself up it will ask you for a username and password. These credentials are
|
itself up it will ask you for a username and password. These credentials are
|
||||||
@@ -51,7 +51,7 @@ I use the windows terminal app to run the Ubuntu distribution, it makes
|
|||||||
customising the environment a lot easier. To download it head over to the
|
customising the environment a lot easier. To download it head over to the
|
||||||
Microsoft store and search for Windows Terminal and download it.
|
Microsoft store and search for Windows Terminal and download it.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
When you login to WSL it will not default to your home directory. To fix that go
|
When you login to WSL it will not default to your home directory. To fix that go
|
||||||
to the Windows Terminal Settings look for Ubuntu or your distributions name and
|
to the Windows Terminal Settings look for Ubuntu or your distributions name and
|
||||||
@@ -61,11 +61,11 @@ change the Starting directory to
|
|||||||
\\wsl$\<distro>\home\<username>
|
\\wsl$\<distro>\home\<username>
|
||||||
```
|
```
|
||||||
|
|
||||||
> Be sure to change "<distro>" to your distributions name
|
> Be sure to change `<distro>` to your distributions name
|
||||||
|
|
||||||
> Be sure to change "<username>" to your username
|
> Be sure to change `<username>` to your username
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## **Conclusion**
|
## **Conclusion**
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Welcome
|
title: Welcome
|
||||||
|
description: The mandatory hello world post — a quick intro to who I am and what this blog is about.
|
||||||
tags:
|
tags:
|
||||||
- blog
|
- blog
|
||||||
- misc
|
- misc
|
||||||
|
|||||||
@@ -1,15 +1,44 @@
|
|||||||
# Caddy as a Reverse Proxy
|
# Caddy as a Reverse Proxy
|
||||||
|
|
||||||
This document describes how to run [Caddy](https://caddyserver.com/) as a
|
This document describes how to run [Caddy](https://caddyserver.com/) as a
|
||||||
reverse proxy in front of the blog. It covers both deployment targets used by
|
reverse proxy in front of the blog.
|
||||||
this project: the Kubernetes (Raspberry Pi) cluster and the Docker Compose host
|
|
||||||
(Gimli).
|
|
||||||
|
|
||||||
It uses the custom [`caddy-cf`](https://github.com/ahmzaa/caddy-cf) image
|
It uses the custom [`caddy-cf`](https://github.com/ahmzaa/caddy-cf) image
|
||||||
(`ghcr.io/ahmzaa/caddy-cf:latest`) — a Caddy build with the **Cloudflare DNS
|
(`ghcr.io/ahmzaa/caddy-cf:latest`) — a Caddy build with the **Cloudflare DNS
|
||||||
module** baked in. The stock Caddy image cannot solve the ACME **DNS-01**
|
module** baked in.
|
||||||
challenge for Cloudflare; this custom build can, which is what lets us obtain
|
|
||||||
certificates without exposing ports `80`/`443` to the public internet.
|
## Deployment topologies
|
||||||
|
|
||||||
|
There are two supported ways to run Caddy in front of the blog. **Pick one.**
|
||||||
|
|
||||||
|
### 1. Cloudflare in front (current production) — HTTP only
|
||||||
|
|
||||||
|
```
|
||||||
|
Cloudflare (terminates TLS) -> Caddy (HTTP :80) -> SWS (blog:80)
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare proxies the domain and provides the public HTTPS certificate, so
|
||||||
|
Caddy only needs to serve plain **HTTP on :80** and reverse-proxy to the blog.
|
||||||
|
Caddy does **not** provision a TLS cert in this mode. This is the live setup and
|
||||||
|
its config is the committed [`Caddyfile`](../Caddyfile) at the repo root —
|
||||||
|
see [Cloudflare-fronted setup](#cloudflare-fronted-setup-http-only) below.
|
||||||
|
|
||||||
|
Set the Cloudflare **SSL/TLS mode to "Full"** so the Cloudflare→origin hop is
|
||||||
|
still encrypted at the network layer (e.g. over Netbird), even though Caddy's
|
||||||
|
listener is HTTP.
|
||||||
|
|
||||||
|
### 2. Caddy terminates TLS via Cloudflare DNS-01
|
||||||
|
|
||||||
|
```
|
||||||
|
Client -> Caddy (HTTPS :443, ACME DNS-01) -> SWS (blog:80)
|
||||||
|
```
|
||||||
|
|
||||||
|
Caddy obtains/renews the certificate itself using the ACME **DNS-01** challenge
|
||||||
|
through the Cloudflare API — no inbound `80`/`443` from the public internet is
|
||||||
|
required, which suits a Netbird-only setup with no Cloudflare proxy in front.
|
||||||
|
The stock Caddy image cannot do this; the `caddy-cf` build bundles the
|
||||||
|
Cloudflare DNS provider plugin. See the Docker Compose / Kubernetes examples
|
||||||
|
later in this document.
|
||||||
|
|
||||||
## Why a reverse proxy?
|
## Why a reverse proxy?
|
||||||
|
|
||||||
@@ -21,7 +50,7 @@ the reverse proxy so that each layer owns exactly one job:
|
|||||||
|
|
||||||
| Concern | Owned by | Notes |
|
| Concern | Owned by | Notes |
|
||||||
| -------------------- | -------- | ------------------------------------------------- |
|
| -------------------- | -------- | ------------------------------------------------- |
|
||||||
| TLS / HTTPS certs | Caddy | Automatic certs via Cloudflare **DNS-01** challenge |
|
| TLS / HTTPS certs | Cloudflare *or* Caddy | Cloudflare in topology 1; Caddy (DNS-01) in topology 2 |
|
||||||
| Compression (gzip/zstd) | Caddy | `encode` directive; SWS compression is off |
|
| Compression (gzip/zstd) | Caddy | `encode` directive; SWS compression is off |
|
||||||
| Security headers | Caddy | Single source of truth, avoids duplicate headers |
|
| Security headers | Caddy | Single source of truth, avoids duplicate headers |
|
||||||
| Serving static files | SWS | Reads from `/public`, sets `Cache-Control`/`ETag` |
|
| Serving static files | SWS | Reads from `/public`, sets `Cache-Control`/`ETag` |
|
||||||
@@ -50,11 +79,72 @@ Whatever the deployment, the SWS origin exposes:
|
|||||||
- **Health endpoint:** `GET /health` → `200 OK`
|
- **Health endpoint:** `GET /health` → `200 OK`
|
||||||
- **Cache headers:** already set by SWS (Caddy should pass them through, not override)
|
- **Cache headers:** already set by SWS (Caddy should pass them through, not override)
|
||||||
|
|
||||||
Caddy only needs to terminate TLS (via Cloudflare DNS-01), add compression +
|
Caddy only needs to handle TLS (or let Cloudflare do it), add compression +
|
||||||
security headers, and forward requests to that origin.
|
security headers, and forward requests to that origin.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Cloudflare-fronted setup (HTTP only)
|
||||||
|
|
||||||
|
**This is the current production topology.** The canonical config is the
|
||||||
|
committed [`Caddyfile`](../Caddyfile) at the repo root; copy it to the
|
||||||
|
`caddy_cf` host and reload. Highlights:
|
||||||
|
|
||||||
|
- **`http://blog.ahmza.com`** — the explicit `http://` scheme binds Caddy to
|
||||||
|
`:80` only and stops it from trying to provision a TLS certificate (Cloudflare
|
||||||
|
already serves HTTPS to visitors).
|
||||||
|
- **`reverse_proxy blog:80`** — proxies to the blog (SWS) container by its
|
||||||
|
compose service name. Caddy and the blog container must share a Docker
|
||||||
|
network. Omitting this responder makes Caddy return an empty `NOP` response —
|
||||||
|
i.e. a **blank page**.
|
||||||
|
- **`encode zstd gzip`** — compression happens here (SWS has it disabled).
|
||||||
|
- **`header { ... }`** — security headers (SWS has them disabled).
|
||||||
|
- **`trusted_proxies cloudflare { ... }`** + **`client_ip_headers
|
||||||
|
Cf-Connecting-Ip X-Forwarded-For`** — so the real visitor IP is used for
|
||||||
|
logging/matchers, but only when the request genuinely comes from Cloudflare
|
||||||
|
(prevents header spoofing from any other source). The `cloudflare` IP source
|
||||||
|
([WeidiDeng/caddy-cloudflare-ip](https://github.com/WeidiDeng/caddy-cloudflare-ip),
|
||||||
|
baked into the `caddy-cf` image) auto-fetches the ranges from
|
||||||
|
<https://www.cloudflare.com/ips-v4/> and <https://www.cloudflare.com/ips-v6/>
|
||||||
|
and refreshes them on the configured `interval`, so there is no static list to
|
||||||
|
maintain:
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
trusted_proxies cloudflare {
|
||||||
|
interval 12h
|
||||||
|
timeout 15s
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`client_ip_headers` lists which header holds the real visitor IP:
|
||||||
|
`Cf-Connecting-Ip` is Cloudflare's single authoritative end-user IP, with
|
||||||
|
`X-Forwarded-For` as the fallback. It is only trusted for requests coming from
|
||||||
|
the `trusted_proxies` above.
|
||||||
|
|
||||||
|
> If you ever run without the `caddy-cloudflare-ip` plugin, you can instead
|
||||||
|
> pin the static ranges with `trusted_proxies static <ip/cidr ...>` copied
|
||||||
|
> from the Cloudflare IP pages, but then you must keep them in sync manually.
|
||||||
|
|
||||||
|
Deploy and reload:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Put the Caddyfile in place on the caddy_cf host (e.g. into the ./conf mount),
|
||||||
|
# then reload without downtime:
|
||||||
|
docker exec caddy_cf caddy reload --config /etc/caddy/Caddyfile
|
||||||
|
|
||||||
|
# Verify from the host (expect HTTP 200, Content-Encoding: gzip, X-Frame-Options):
|
||||||
|
curl -sI -H "Accept-Encoding: gzip" http://localhost/ | grep -iE 'HTTP|content-encoding|x-frame'
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Cloudflare SSL/TLS mode:** set it to **Full** so the Cloudflare→origin hop
|
||||||
|
> is encrypted at the network layer (e.g. over Netbird), even though Caddy's
|
||||||
|
> listener is plain HTTP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The remaining sections describe **topology 2** (Caddy terminates TLS itself via
|
||||||
|
the Cloudflare DNS-01 challenge) for setups without a Cloudflare proxy in front.
|
||||||
|
|
||||||
## Docker Compose (Gimli)
|
## Docker Compose (Gimli)
|
||||||
|
|
||||||
Run Caddy and the blog container on the same Docker network. Caddy reaches the
|
Run Caddy and the blog container on the same Docker network. Caddy reaches the
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ module github.com/ahmzaa/blog-ahmza-com
|
|||||||
|
|
||||||
go 1.24.3
|
go 1.24.3
|
||||||
|
|
||||||
require github.com/nunocoracao/blowfish/v2 v2.86.0 // indirect
|
require github.com/nunocoracao/blowfish/v3 v3.4.0
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/nunocoracao/blowfish/v3 v3.4.0 h1:Fv7mS97lagllTGzHA2V1H6+N5fuQd3DuWmOQlFFV4Aw=
|
||||||
|
github.com/nunocoracao/blowfish/v3 v3.4.0/go.mod h1:8lJwR95VgcyWDhjKvUJMdOQXogrmhYSxkWh4ogSJfCg=
|
||||||
@@ -1 +1 @@
|
|||||||
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank"{{ end }}>{{ .Text | safeHTML }}</a>
|
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener noreferrer"{{ end }}>{{ .Text | safeHTML }}</a>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<br>
|
<br>
|
||||||
<div class="flex items-center gap-x-5">
|
<div class="flex items-center gap-x-5">
|
||||||
<a class="text-sm text-neutral-500 dark:text-neutral-400 rel='me'" href="https://mastodon.social/@ahmza">Mastodon</a>
|
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://mastodon.social/@ahmza">Mastodon</a>
|
||||||
<a class="text-sm text-neutral-500 dark:text-neutral-400 rel='me'" href="https://github.com/ahmzaa">Github</a>
|
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://www.linkedin.com/in/ahmza/">Linkedin</a>
|
||||||
<a class="text-sm text-neutral-500 dark:text-neutral-400 rel='me'" href="https://www.linkedin.com/in/ahmza/">Linkedin</a>
|
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://github.com/ahmzaa">Github</a>
|
||||||
|
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://gitlab.com/ahmza">Gitlab</a>
|
||||||
|
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://tea.ahmza.com/ahmza">Gitea</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<img src="{{ .Get "url"}}" alt="{{ .Get "alt"}}" style="display:block;margin-left:auto;margin-right:auto;width:50%;height:auto;padding:10px;">
|
<img src="{{ .Get "url"}}" alt="{{ .Get "alt"}}" loading="lazy" decoding="async" style="display:block;margin-left:auto;margin-right:auto;width:50%;height:auto;padding:10px;">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<video autoplay loop muted playsinline aria-label='{{ .Get "label"}}' style="display:block;margin-left:auto;margin-right:auto;width:50%;height:auto;padding:10px;">
|
<video autoplay loop muted playsinline preload="metadata" aria-label='{{ .Get "label"}}' style="display:block;margin-left:auto;margin-right:auto;width:50%;height:auto;padding:10px;">
|
||||||
{{ with .Get "mp4" }}<source src="{{ . }}" type="video/mp4">{{ end }}
|
{{ with .Get "mp4" }}<source src="{{ . }}" type="video/mp4">{{ end }}
|
||||||
{{ with .Get "webm" }}<source src="{{ . }}" type="video/webm">{{ end }}
|
{{ with .Get "webm" }}<source src="{{ . }}" type="video/webm">{{ end }}
|
||||||
</video>
|
</video>
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
## Commit messages
|
|
||||||
backend:
|
|
||||||
name: github
|
|
||||||
repo: ahmzaa/blog-ahmza-com
|
|
||||||
branch: main
|
|
||||||
squash_merges: true
|
|
||||||
base_url: https://sveltia-cms-auth.hamzaahmed12328.workers.dev
|
|
||||||
use_graphql: true
|
|
||||||
commit_messages:
|
|
||||||
create: Create {{collection}} "{{slug}}"
|
|
||||||
update: Update {{collection}} "{{slug}}"
|
|
||||||
delete: Delete {{collection}} "{{slug}}"
|
|
||||||
uploadMedia: Upload "{{path}}"
|
|
||||||
deleteMedia: Delete "{{path}}"
|
|
||||||
openAuthoring: '{{message}}'
|
|
||||||
|
|
||||||
## Publish mode
|
|
||||||
publish_mode: editorial_workflow # Allowed Values: simple, editorial_workflow
|
|
||||||
|
|
||||||
## Media and Public folders
|
|
||||||
#Media
|
|
||||||
media_folder: "assets/uploaded/img"
|
|
||||||
public_folder: "/uploaded/img"
|
|
||||||
|
|
||||||
## SiteTitle
|
|
||||||
site_url: htpps://blog.ahmza.com
|
|
||||||
display_url: blog.ahmza.com
|
|
||||||
logo_url: https://blog.ahmza.com/logo.webp
|
|
||||||
|
|
||||||
## Collections
|
|
||||||
collections:
|
|
||||||
- name: posts
|
|
||||||
identifier_field: name
|
|
||||||
label: posts
|
|
||||||
label_singular: post
|
|
||||||
icon: article
|
|
||||||
description: sites main content, blog posts
|
|
||||||
folder: "content/posts"
|
|
||||||
path: "{{slug}}/index"
|
|
||||||
media_folder: ""
|
|
||||||
public_folder: ""
|
|
||||||
create: true
|
|
||||||
slug: '{{slug}}'
|
|
||||||
fields:
|
|
||||||
- {label: "Title", name: "title", widget: "string"}
|
|
||||||
- {label: "Featured Image", name: "featureimage", widget: "image"}
|
|
||||||
- {label: "Featured Image Caption", name: "featureimagecaption", widget: "string"}
|
|
||||||
- {label: "Publish Date", name: "date", widget: "datetime", default: "{{now}}", date_format: "YYYY-MM-DD"}
|
|
||||||
- {label: "Last Modified", name: "lastmod", widget: "datetime", default: "{{now}}", date_format: "YYYY-MM-DD"}
|
|
||||||
- {label: "Author", name: "author", widget: "string", default: "ahmza"}
|
|
||||||
- {label: "Draft", name: "draft", widget: "boolean", default: "false"}
|
|
||||||
- {label: "Tags", name: "tags", widget: "list"}
|
|
||||||
- {label: "Description", name: "description", widget: "string"}
|
|
||||||
- {label: "Body", name: "body", widget: "markdown"}
|
|
||||||
extension: md
|
|
||||||
sortable_fields: ['title', 'date', 'lastmod', 'tags']
|
|
||||||
view_filters:
|
|
||||||
- label: "ahmza's posts"
|
|
||||||
field: author
|
|
||||||
pattern: 'ahmza'
|
|
||||||
- label: Drafts
|
|
||||||
field: draft
|
|
||||||
pattern: true
|
|
||||||
view_groups:
|
|
||||||
- label: Year
|
|
||||||
field: date
|
|
||||||
pattern: \d{4}
|
|
||||||
- label: Drafts
|
|
||||||
field: draft
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="robots" content="noindex" />
|
|
||||||
<title>Content Manager</title>
|
|
||||||
<!-- Include the script that enables Netlify Identity on this page. -->
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- Include the script that builds the page and powers Decap CMS -->
|
|
||||||
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -31,9 +31,10 @@ cache-control-headers = true
|
|||||||
|
|
||||||
#### Routing
|
#### Routing
|
||||||
|
|
||||||
# SPA-style fallback: serve index.html when a path is not found on disk,
|
# No SPA-style page fallback: this is a static Hugo site, not a single-page app.
|
||||||
# mirroring the previous nginx `try_files $uri $uri/ /index.html`.
|
# Missing paths should return Hugo's generated 404.html with a real 404 status
|
||||||
page-fallback = "/public/index.html"
|
# (SWS serves /public/404.html automatically) rather than masking broken links
|
||||||
|
# with a 200 homepage response, which harms SEO (soft 404s).
|
||||||
|
|
||||||
#### Security
|
#### Security
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user