Improve title on recipes

This commit is contained in:
David Young committed 2022-07-10 20:44:18 +12:00
1 parent 346c3a9c85
commit bb2b358ec6
41 files changed
+1057 -31

No files matched your search

+1
View File
@@ -4,6 +4,7 @@ FROM ${FROM_SOURCE}
RUN apk add --no-cache py3-pip py3-pillow py3-cffi py3-brotli gcc musl-dev python3-dev pango build-base libffi-dev jpeg-dev libxslt-dev RUN apk add --no-cache py3-pip py3-pillow py3-cffi py3-brotli gcc musl-dev python3-dev pango build-base libffi-dev jpeg-dev libxslt-dev
RUN pip install \ RUN pip install \
beautifulsoup4==4.9.3 \
mkdocs-autolinks-plugin \ mkdocs-autolinks-plugin \
mkdocs-htmlproofer-plugin \ mkdocs-htmlproofer-plugin \
mkdocs-git-revision-date-localized-plugin \ mkdocs-git-revision-date-localized-plugin \
+3
View File
@@ -1,3 +1,6 @@
---
title: Pre-made ansible playbooks to deploy our self-hosted recipes
---
# Premix Repository # Premix Repository
"Premix" is a private repository shared with [GitHub sponsors](https://github.com/sponsors/funkypenguin), which contains the necessary files and automation to quickly deploy any recipe, or even an entire [swarm](/docker-swarm/) / [cluster](/kubernetes/)! :muscle: "Premix" is a private repository shared with [GitHub sponsors](https://github.com/sponsors/funkypenguin), which contains the necessary files and automation to quickly deploy any recipe, or even an entire [swarm](/docker-swarm/) / [cluster](/kubernetes/)! :muscle:
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: A self-hosted internet archiving solution title: Archivebox - bookmark manager for your self-hosted stack
--- ---
# Archivebox # Archivebox
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
title: Run linuxserver bookstack in Docker title: How to run BookStack in Docker
description: BookStack is a simple, self-hosted, easy-to-use platform for organising and storing information. Here's how to integrate linuxserver's bookstack image into your Docker Swarm stack. description: BookStack is a simple, self-hosted, easy-to-use platform for organising and storing information. Here's how to integrate linuxserver's bookstack image into your Docker Swarm stack.
--- ---
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Duplicati - Yet another option to backup your exciting stuff. It's good to have options. title: Use Duplicati in Docker to backup to backblaze / b2 and friends
description: Duplicati - Yet another boring option to backup your exciting stuff, especially to Backblaze / B2 - It's good to have options.
--- ---
# Duplicati # Duplicati
+1
View File
@@ -1,4 +1,5 @@
--- ---
title: Use Duplicity in Docker to backup to backblaze / b2 and friends
description: A boring recipe to backup your exciting stuff. Boring is good. description: A boring recipe to backup your exciting stuff. Boring is good.
--- ---
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Real heroes backup their shizz! title: Use elkarbackup in Docker to backup to backblaze / b2 and friends
description: ElkarBackup is a free open-source backup solution based on RSync/RSnapshot. It's basically a web wrapper around rsync/rsnapshot, which means that your backups are just files on a filesystem, utilising hardlinks for tracking incremental changes.
--- ---
# Elkar Backup # Elkar Backup
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Ghost - Beautiful online publicatio (who you gonna call?) title: Blog with Ghost in Docker
description: How to run the beautiful, publication-focused blogging engine "Ghost" using Docker
--- ---
# Ghost # Ghost
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: Gitlab Runner - A gopher for GitLab title: How to run Gitlab Runner in Docker
--- ---
# Gitlab Runner # Gitlab Runner
+3
View File
@@ -1,3 +1,6 @@
---
title: How to run Gitlab in Docker
---
# GitLab # GitLab
GitLab is a self-hosted [alternative to GitHub](https://about.gitlab.com/pricing/self-managed/feature-comparison/). The most common use case is (a set of) developers with the desire for the rich feature-set of GitHub, but with unlimited private repositories. GitLab is a self-hosted [alternative to GitHub](https://about.gitlab.com/pricing/self-managed/feature-comparison/). The most common use case is (a set of) developers with the desire for the rich feature-set of GitHub, but with unlimited private repositories.
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: Gollum - A recipe for your own git-based wiki. My preciousssss title: Run Gollum in Docker
--- ---
# Gollum # Gollum
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: A self-hosted, hackable version of IFFTT / Zapier title: Run Huggin in Docker
--- ---
# Huginn # Huginn
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Automate your fake Instagram life with automated fakery! title: How to run InstaPy in Docker
description: Automate your fake Instagram life with automated fakery using InstaPy in Docker
--- ---
# InstaPy # InstaPy
+5 -1
View File
@@ -1,3 +1,7 @@
---
title: How to build an IPFS cluster in Docker
description: IPFS is a peer-to-peer distributed file system that seeks to connect all computing devices with the same system of files. In some ways, IPFS is similar to the World Wide Web, but IPFS could be seen as a single BitTorrent swarm, exchanging objects within one Git repository.
---
# IPFS # IPFS
!!! danger "This recipe is a work in progress" !!! danger "This recipe is a work in progress"
@@ -8,7 +12,7 @@ The intention of this recipe is to provide a local IPFS cluster for the purpose
![IPFS Screenshot](../images/ipfs.png){ loading=lazy } ![IPFS Screenshot](../images/ipfs.png){ loading=lazy }
Description. IPFS is a peer-to-peer distributed file system that seeks to connect all computing devices with the same system of files. In some ways, IPFS is similar to the World Wide Web, but IPFS could be seen as a single BitTorrent swarm, exchanging objects within one Git repository. IPFS is a peer-to-peer distributed file system that seeks to connect all computing devices with the same system of files. In some ways, IPFS is similar to the World Wide Web, but IPFS could be seen as a single BitTorrent swarm, exchanging objects within one Git repository.
## Ingredients ## Ingredients
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Get your personal kanban on! title: How to run Kanboard using Docker
description: Run Kanboard with Docker to get your personal kanban on!
--- ---
# Kanboard # Kanboard
@@ -1,3 +1,7 @@
---
title: Integrate LDAP server with Keycloak for user federation
description: Here's how we'll add an LDAP provider to our KeyCloak server for user federation.
---
# Authenticate KeyCloak against OpenLDAP # Authenticate KeyCloak against OpenLDAP
!!! warning !!! warning
+5 -1
View File
@@ -1,4 +1,8 @@
# Create KeyCloak Users ---
title: Create users in Keycloak
description: Unless you plan to authenticate against an outside provider (OpenLDAP, below, for example_), you'll want to create some local users..
---
# Create Keycloak Users
!!! warning !!! warning
This is not a complete recipe - it's an optional component of the [Keycloak recipe](/recipes/keycloak/), but has been split into its own page to reduce complexity. This is not a complete recipe - it's an optional component of the [Keycloak recipe](/recipes/keycloak/), but has been split into its own page to reduce complexity.
+1
View File
@@ -1,4 +1,5 @@
--- ---
title: How to setup OIDC server in Docker with KeyCloak
description: Kick-ass OIDC and identity management description: Kick-ass OIDC and identity management
--- ---
@@ -1,3 +1,7 @@
---
title: How to setup OIDC provider in KeyCloak
description: Having an authentication provider is not much use until you start authenticating things against it! In order to authenticate against KeyCloak using OpenID Connect (OIDC), which is required for Traefik Forward Auth, we'll setup a client in KeyCloak...
---
# Add OIDC Provider to KeyCloak # Add OIDC Provider to KeyCloak
!!! warning !!! warning
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Bazinga! Comic book management FTW! title: How to run Komga with Docker
description: Run Komga under Docker Swarm in docker-compose syntax
--- ---
# Komga # Komga
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: A self-contained mailserver with spam-fighting friends title: Run postfix / dovecot with docker-mailserver
description: A self-contained mailserver (postfix, dovecot) in Docker with spam-fighting friends (spamassassin, clamav)
--- ---
# Mail Server # Mail Server
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: A lightweight minimalist RSS reader title: Read RSS in peace with miniflux on Docker
--- ---
# Miniflux # Miniflux
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Self-hosted S3-compatible object storage title: Run Minio on Docker (using compose format in swarm)
description: How to run Minio's self-hosted S3-compatible object storage under Docker Swarm, using docker-compose v3 syntax
--- ---
# Minio # Minio
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Share docs. Backup files. Share stuff. title: How to run Nextcloud in Docker (behind Traefik)
description: We can now run Nextcloud in our Docker Swarm, with LetsEncrypt SSL termination handled by Traefik
--- ---
# NextCloud # NextCloud
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Authenticate like it's 1990! title: Run OpenLDAP in Docker
description: How to run an OpenLDAP server in Docker Swarm, with LDAP Account Manager. Authenticate like it's 1990!
--- ---
# OpenLDAP # OpenLDAP
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: Spy on yourself. title: Run OwnTracks under Docker
--- ---
# OwnTracks # OwnTracks
+3 -2
View File
@@ -1,8 +1,9 @@
--- ---
description: Easily index, search, and view archive all of your scanned dead-tree documents title: Run paperless-ngx under Docker
description: Easily index, search, and view archive all of your scanned dead-tree documents with Paperless NGX, under Docker
--- ---
# Paperless NG # Paperless NGX
Paper is a nightmare. Environmental issues aside, there’s no excuse for it in the 21st century. It takes up space, collects dust, doesn’t support any form of a search feature, indexing is tedious, it’s heavy and prone to damage & loss. [^1] Paperless NG will OCR, index, and store data about your documents so they are easy to search and view, unlike that hulking metal file cabinet you have in your office. Paper is a nightmare. Environmental issues aside, there’s no excuse for it in the 21st century. It takes up space, collects dust, doesn’t support any form of a search feature, indexing is tedious, it’s heavy and prone to damage & loss. [^1] Paperless NG will OCR, index, and store data about your documents so they are easy to search and view, unlike that hulking metal file cabinet you have in your office.
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Is that IP address in use? title: Run phpIPAM under Docker
description: Is that IP address in use? Do some DHP / Discovery with phpIPAM under Docker
--- ---
# phpIPAM # phpIPAM
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: A UI to make Docker less geeky title: Run Portainer in Docker Swarm (now with Dark Mode!)
description: Portainer is a UI to make Docker less geeky, runs under Docker Swarm (and Kubernetes!) and most importantly, now supports dark mode!
--- ---
# Portainer # Portainer
+1
View File
@@ -1,4 +1,5 @@
--- ---
title: Realms is a git-based wiki, and it runs under Docker!
description: A git-based wiki with auth and registration description: A git-based wiki with auth and registration
--- ---
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: Read-it-later, mate! title: Run Wallabag under Docker (compose), mate!
--- ---
# Wallabag # Wallabag
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
description: A self-hosted Trello title: Run Wekan under Docker
--- ---
# Wekan # Wekan
+2 -1
View File
@@ -1,5 +1,6 @@
--- ---
description: Terminal in a browser, baby! title: Use wetty under Docker for SSH in the browser
description: Use weTTY to run a terminal in a browser, baby!
--- ---
# Wetty # Wetty
+405
View File
@@ -0,0 +1,405 @@
site_name: Funky Penguin's Geek Cookbook
site_description: 'The Geek Cookbook is a collection of guides for establishing your own highly-available docker container cluster (swarm). This swarm enables you to run self-hosted services such as GitLab, Plex, NextCloud, etc.'
site_author: 'David Young'
site_url: 'https://geek-cookbook.funkypenguin.co.nz'
edit_uri: 'edit/master/manuscript/'
# Fail to build if we have broken links
# strict: true
# Repository
repo_name: 'geek-cookbook'
repo_url: 'https://github.com/geek-cookbook/geek-cookbook'
# Set this to "manuscript" so that we can produce leanpub content
docs_dir: 'manuscript'
# Copyright
copyright: 'Copyright © 2016 - 2021 David Young, Funky Penguin Limited'
# Plugins
plugins:
privacy:
search:
minify:
minify_html: true
git-revision-date-localized:
type: date
autolinks:
macros:
verbose: true
# htmlproofer:
# raise_error: True
# raise_error_excludes:
# 504: ['https://www.mkdocs.org/']
# 404: ['https://github.com/manuzhang/mkdocs-htmlproofer-plugin']
# 400: ['*']
git-committers:
repository: geek-cookbook/geek-cookbook
token: !ENV GH_TOKEN
meta-descriptions: # If not provided, auto-generate a description (https://pypi.org/project/mkdocs-meta-descriptions-plugin/)
#theme_dir: mkdocs-material
nav:
- Home: index.md
- Docker Swarm:
- docker-swarm/index.md
- Preparation:
- Design: docker-swarm/design.md
- Nodes: docker-swarm/nodes.md
- Shared Storage (Ceph): docker-swarm/shared-storage-ceph.md
- Shared Storage (GlusterFS): docker-swarm/shared-storage-gluster.md
- Keepalived: docker-swarm/keepalived.md
- Docker Swarm Mode: docker-swarm/docker-swarm-mode.md
- Traefik: docker-swarm/traefik.md
- Traefik Forward Auth:
- docker-swarm/traefik-forward-auth/index.md
- Dex (static): docker-swarm/traefik-forward-auth/dex-static.md
- Google: docker-swarm/traefik-forward-auth/google.md
- KeyCloak: docker-swarm/traefik-forward-auth/keycloak.md
- Authelia: docker-swarm/authelia.md
- Registry: docker-swarm/registry.md
- Mail Server: recipes/mail.md
- Duplicity: recipes/duplicity.md
- Chef's Favorites:
- Auto Pirate:
- recipes/autopirate/index.md
- Headphones: recipes/autopirate/headphones.md
- Heimdall: recipes/autopirate/heimdall.md
- Jackett: recipes/autopirate/jackett.md
- Lazy Librarian: recipes/autopirate/lazylibrarian.md
- Lidarr: recipes/autopirate/lidarr.md
- Mylar: recipes/autopirate/mylar.md
- NZBGet: recipes/autopirate/nzbget.md
- NZBHydra: recipes/autopirate/nzbhydra.md
- Prowlarr: recipes/autopirate/prowlarr.md
- Ombi: recipes/autopirate/ombi.md
- Radarr: recipes/autopirate/radarr.md
- Readarr: recipes/autopirate/readarr.md
- Rtorrent: recipes/autopirate/rtorrent.md
- SABnzbd: recipes/autopirate/sabnzbd.md
- Sonarr: recipes/autopirate/sonarr.md
- End: recipes/autopirate/end.md
- Emby: recipes/emby.md
- Home Assistant: recipes/homeassistant.md
- Huginn: recipes/huginn.md
- Jellyfin: recipes/jellyfin.md
- Kanboard: recipes/kanboard.md
- KeyCloak:
- recipes/keycloak/index.md
- Users: recipes/keycloak/create-user.md
- OIDC Provider: recipes/keycloak/setup-oidc-provider.md
- OpenLDAP: recipes/keycloak/authenticate-against-openldap.md
- Miniflux: recipes/miniflux.md
- Munin: recipes/munin.md
- NextCloud: recipes/nextcloud.md
- phpIPAM: recipes/phpipam.md
- Plex: recipes/plex.md
- Portainer: recipes/portainer.md
- PrivateBin: recipes/privatebin.md
- Restic: recipes/restic.md
- Swarmprom: recipes/swarmprom.md
- Recipes:
- Archive Box: recipes/archivebox.md
- Bitwarden: recipes/bitwarden.md
- Bookstack: recipes/bookstack.md
- Calibre-Web: recipes/calibre-web.md
- Collabora Online: recipes/collabora-online.md
- CyberChef: recipes/cyberchef.md
- Duplicati: recipes/duplicati.md
- ElkarBackup: recipes/elkarbackup.md
- Funkwhale: recipes/funkwhale.md
- Ghost: recipes/ghost.md
- GitLab: recipes/gitlab.md
- GitLab Runner: recipes/gitlab-runner.md
- Gollum: recipes/gollum.md
- InstaPy: recipes/instapy.md
- KeyCloak:
- recipes/keycloak/index.md
- Users: recipes/keycloak/create-user.md
- OIDC Provider: recipes/keycloak/setup-oidc-provider.md
- OpenLDAP: recipes/keycloak/authenticate-against-openldap.md
- Komga: recipes/komga.md
- Linx: recipes/linx.md
- Mealie: recipes/mealie.md
- Minio: recipes/minio.md
- NightScout: recipes/nightscout.md
- OpenLDAP: recipes/openldap.md
- OwnTracks: recipes/owntracks.md
- Photoprism: recipes/photoprism.md
- Paperless NG: recipes/paperless-ng.md
- Portainer: recipes/portainer.md
- Realms: recipes/realms.md
- Restic: recipes/restic.md
- RSS Bridge: recipes/rss-bridge.md
- Tiny Tiny RSS: recipes/tiny-tiny-rss.md
- Traefik: docker-swarm/traefik.md
- Traefik Forward Auth:
- docker-swarm/traefik-forward-auth/index.md
- Dex (static): docker-swarm/traefik-forward-auth/dex-static.md
- Google: docker-swarm/traefik-forward-auth/google.md
- KeyCloak: docker-swarm/traefik-forward-auth/keycloak.md
- Wallabag: recipes/wallabag.md
- Wekan: recipes/wekan.md
- Wetty: recipes/wetty.md
- Reference:
- OAuth Proxy: reference/oauth_proxy.md
- Data Layout: reference/data_layout.md
- Networks: reference/networks.md
- OpenVPN : reference/openvpn.md
- Troubleshooting: reference/troubleshooting.md
- ⛴ Kubernetes:
- Preparation:
- kubernetes/index.md
- Introduction: kubernetes/index.md
- Cluster:
- kubernetes/cluster/index.md
- Digital Ocean: kubernetes/cluster/digitalocean.md
# - Bare Metal: kubernetes/cluster/baremetal.md
# - Home Lab: kubernetes/cluster/baremetal.md
- k3s: kubernetes/cluster/k3s.md
# - The Hard Way: kubernetes/cluster/the-hard-way.md
- Deployment:
- kubernetes/deployment/index.md
# - YAML: kubernetes/wip.md
# - Helm: kubernetes/wip.md
# - GitHub Actions: kubernetes/wip.md
- Flux:
- kubernetes/deployment/flux/index.md
- Install: kubernetes/deployment/flux/install.md
- Design: kubernetes/deployment/flux/design.md
- Operate: kubernetes/deployment/flux/operate.md
- Essentials:
- Load Balancer:
- kubernetes/loadbalancer/index.md
- k3s: kubernetes/loadbalancer/k3s.md
- MetalLB:
- kubernetes/loadbalancer/metallb/index.md
- pfSense: kubernetes/loadbalancer/metallb/pfsense.md
- Sealed Secrets: kubernetes/sealed-secrets.md
- External DNS: kubernetes/external-dns.md
- SSL Certificates:
- kubernetes/ssl-certificates/index.md
- Cert-Manager: kubernetes/ssl-certificates/cert-manager.md
- LetsEncrypt Issuers: kubernetes/ssl-certificates/letsencrypt-issuers.md
- Wildcard Certificate: kubernetes/ssl-certificates/wildcard-certificate.md
- Secret Replicator: kubernetes/ssl-certificates/secret-replicator.md
- Ingress:
- kubernetes/ingress/index.md
- Traefik:
- kubernetes/ingress/traefik/index.md
# - Dashboard: kubernetes/ingress/traefik/dashboard.md
- Nginx: kubernetes/ingress/nginx.md
- Persistence:
- kubernetes/persistence/index.md
- Local Path Provisioner: kubernetes/persistence/local-path-provisioner.md
- TopoLVM: kubernetes/persistence/topolvm.md
# - Rook Ceph: kubernetes/persistence/rook-ceph.md
# - OpenEBS: kubernetes/persistence/openebs.md
# - LongHorn: kubernetes/persistence/longhorn.md
# - Backup:
# - kubernetes/backup/index.md
# - kubernetes/wip.md
# - Logging:
# - fluent-bit, graylog, etc.
# - Monitoring:
# - kubernetes/monitoring/index.md
# - Prometheus: kubernetes/wip.md
# - Grafana: kubernetes/wip.md
# - AlertManager: kubernetes/wip.md
# - Goldilocks: kubernetes/wip.md
# - Reloader: kubernetes/wip.md
# - Dashboard: kubernetes/wip.md
# - Kured: kubernetes/wip.md
# - KeyCloak: kubernetes/wip.md
# - Recipes:
# - Harbor:
# - recipes/kubernetes/harbor/index.md
# Istio: recipes/kubernetes/harbor/istio.md
# - GitHub Actions Runners: kubernetes/wip.md
# - Cilium: kubernetes/wip.md
# - Concourse: kubernetes/wip.md
# - Flagger: kubernetes/wip.md
# - Flagger: kubernetes/wip.md
# - Flux: recipes/kubernetes/wip.md
# - FoundationDB: kubernetes/wip.md
# - Istio: recipes/kubernetes/wip.md
# - Jaeger: kubernetes/wip.md
# - Kiali: kubernetes/wip.md
# - Minio: kubernetes/wip.md
# - NGINX Ingress: kubernetes/wip.md
# - Polaris: kubernetes/wip.md
# - Portainer: kubernetes/wip.md
# - Prometheus: kubernetes/wip.md
# - Traefik: kubernetes/wip.md
# - Vault: kubernetes/wip.md
# - Webook Receiver: kubernetes/wip.md
- 🚀 Get Premix!:
- premix/index.md
- Operation: premix/ansible/operation.md
- Design: premix/ansible/design.md
# - Swarm: premix/swarm.md
# - Kubernetes: premix/kubernetes.md
- CHANGELOG: CHANGELOG.md
- Support: support.md
- Contribute: community/contribute.md
- Community:
- Discord: community/discord.md
- Reddit: community/reddit.md
- Support: support.md
- Contribute: community/contribute.md
- Code of Conduct: community/code-of-conduct.md
# # - Discourse: community/discourse.md
# # - GitHub: community/github.md
theme:
name: 'material'
custom_dir: 'overrides'
# disqus: 'geeks-cookbook'
logo: 'images/site-logo.svg'
favicon: 'images/site-logo.svg'
features:
- navigation.tabs
- navigation.tabs.sticky
- navigation.instant
- navigation.sections
- navigation.indexes
- navigation.top
- navigation.pruning
- search.suggest
- search.share
- content.code.annotate
- header.autohide
- announce.dismiss
- toc.follow
icon:
repo: 'fontawesome/brands/github'
palette:
# Palette toggle for automatic mode
- media: "(prefers-color-scheme)"
toggle:
icon: material/brightness-auto
name: Switch to light mode
# Toggle light mode
- media: "(prefers-color-scheme: light)"
scheme: default
primary: 'black'
accent: 'orange'
toggle:
icon: material/toggle-switch
name: Burn my eyes!
# Toggle dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: 'black'
accent: 'orange'
toggle:
icon: material/toggle-switch-off-outline
name: Love my eyes!
font:
text: 'Ubuntu'
code: 'Ubuntu Mono'
extra:
social:
- icon: 'fontawesome/brands/github'
link: 'https://github.com/funkypenguin'
- icon: 'fontawesome/brands/twitter'
link: 'https://twitter.com/funkypenguin'
- icon: 'fontawesome/brands/stack-overflow'
link: 'https://stackoverflow.com/cv/funkypenguin'
- icon: 'material/rss'
link: 'https://www.funkypenguin.co.nz/'
- icon: 'fontawesome/brands/linkedin'
link: 'https://www.linkedin.com/in/funkypenguin'
- icon: 'material/mailbox'
link: 'mailto:davidy@funkypenguin.co.nz?Subject=Hello%2C%20from%20a%20geek-cookbook%20reader%20%3B%29'
generator: false
# Plausible doesn't use cookies, so no need for this!
# consent:
# title: 'Would you like a cookie? 🍪'
# description: >
# I know. cookies suck (<I>except the fresh, warm, choc-chip ones!</I>), and so does invasive tracking. Thing is, without cookies,
# analytics can't record your visit, and I can't work out where/how to invest time to make the cookbook better.
# <p>So, with your consent, I'd like to record what you look at and where you're from, so that I can make the
# cookbook even better. My analytics are public - here's <A HREF='https://plausible.io/geek-cookbook.funkypenguin.co.nz' target="_blank">all</A> I know about you! - David</p>
# cookies:
# custom: Plausible Analytics
analytics:
provider: plausible
data_domain: geek-cookbook.funkypenguin.co.nz
feedback:
title: Was this page helpful?
ratings:
- icon: material/emoticon-happy-outline
name: This page was helpful
data: 1
note: >-
Thanks for your feedback!
- icon: material/emoticon-sad-outline
name: This page could be improved
data: 0
note: >-
Thanks for your feedback!
# Help us improve this page by
# using our <a href="..." target=_blank>feedback form</a>.
extra_css:
- extras/css/icons.css
extra_javascript:
- extras/javascript/discord.js
- extras/javascript/sortable-tables.js
- extras/javascript/feedback.js
- https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js
# Extensions
markdown_extensions:
- attr_list
- admonition
- codehilite:
linenums: true
- toc:
permalink: true
- footnotes
- pymdownx.critic
- pymdownx.arithmatex
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.details
- pymdownx.snippets:
check_paths: true
base_path: _snippets
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.inlinehilite
- pymdownx.magiclink:
repo_url_shorthand: true
user: funkypenguin
repo: geek-cookbook
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.tilde
- pymdownx.tabbed
- meta
- md_in_html
+9 -3
View File
@@ -13,7 +13,7 @@ plugins:
back_cover: true back_cover: true
#cover_title: TITLE TEXT #cover_title: TITLE TEXT
#cover_subtitle: SUBTITLE TEXT #cover_subtitle: SUBTITLE TEXT
#custom_template_path: TEMPLATES PATH # custom_template_path: pdf_templates
#toc_title: TOC TITLE TEXT #toc_title: TOC TITLE TEXT
#heading_shift: true #heading_shift: true
toc_level: 3 toc_level: 3
@@ -30,10 +30,16 @@ plugins:
text: ALTERNATE TEXT text: ALTERNATE TEXT
- src: ... - src: ...
# two_columns_level: 3 # two_columns_level: 3
render_js: true render_js: false
# headless_chrome_path: headless-chromium headless_chrome_path: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
output_path: funky-penguins-geek-cookbook.pdf output_path: funky-penguins-geek-cookbook.pdf
debug_html: true debug_html: true
show_anchors: true show_anchors: true
verbose: true verbose: true
extra_css: []
extra_javascript: []
theme:
font: false
Binary file not shown.
+528
View File
@@ -0,0 +1,528 @@
<html><head><meta charset="utf-8"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="My collection of how-to guides and tutorials for establishing your own container-based awesome selfhosted platform, using either Docker or Kubernetes." name="description"/><meta content="David Young" name="author"/><link href="https://geek-cookbook.funkypenguin.co.nz/" rel="canonical"/><link href="file:///Users/davidy/Documents/Personal/Projects/geek-cookbook/site/images/site-logo.svg" rel="icon"/><meta content="mkdocs-1.3.0, mkdocs-material-8.3.9" name="generator"/><title>How I do "awesome selfhosted" |・∀・</title><link href="file:///Users/davidy/Documents/Personal/Projects/geek-cookbook/site/assets/stylesheets/main.1d29e8d0.min.css" rel="stylesheet"/><link href="file:///Users/davidy/Documents/Personal/Projects/geek-cookbook/site/assets/stylesheets/palette.cbb835fc.min.css" rel="stylesheet"/><script>__md_scope=new URL(".",location),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script><!-- <script src="https://cdn.jsdelivr.net/npm/@widgetbot/crate@3" async defer></script> --><script src="/js/i-am-groot.js"></script><script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script><!-- RightMessage --><script type="text/javascript">
(function(p, a, n, d, o, b, c) {
o = n.createElement('script'); o.type = 'text/javascript'; o.async = true; o.src = 'https://tb.rightmessage.com/'+p+'.js';
b = n.getElementsByTagName('script')[0]; d = function(h, u, i) { var c = n.createElement('style'); c.id = 'rmcloak'+i;
c.type = 'text/css'; c.appendChild(n.createTextNode('.rmcloak'+h+'{visibility:hidden}.rmcloak'+u+'{display:none}'));
b.parentNode.insertBefore(c, b); return c; }; c = d('', '-hidden', ''); d('-stay-invisible', '-stay-hidden', '-stay');
setTimeout(o.onerror = function() { c.parentNode && c.parentNode.removeChild(c); }, a); b.parentNode.insertBefore(o, b);
})('1802694484', 20000, document);
</script><link href="file:///Users/davidy/Documents/Personal/Projects/geek-cookbook/site/assets/stylesheets/extra-style.w7lhf_wh.min.css" rel="stylesheet"/><style>@charset "UTF-8";
:root {
string-set: author 'David Young', copyright 'Copyright © 2016 - 2021 David Young, Funky Penguin Limited', title 'Funky Penguin\27s Geek Cookbook'; }
h1, h2, h3 {
string-set: chapter content(); }
@page {
size: a4 portrait;
margin: 25mm 10mm 25mm 10mm;
counter-increment: page;
white-space: pre;
color: grey;
@top-right {
font-size: 7pt;
content: string(chapter); }
@bottom-center {
font-size: 8pt;
content: "- " counter(page) "/" counter(pages) " -"; }
@bottom-right {
font-size: 6pt;
content: string(copyright); } }
@media print {
body {
background: none;
background-color: #fff; } }
article,
section + section {
page-break-before: always;
min-height: 100vh;
/* Defining all page breaks */ }
article,
article h1,
article h2,
article h3,
article h4,
article h5,
article h6,
article div.tabbed-content--wrap > .tabbed-content,
section + section,
section + section h1,
section + section h2,
section + section h3,
section + section h4,
section + section h5,
section + section h6,
section + section div.tabbed-content--wrap > .tabbed-content {
clear: both; }
article h1,
article h2,
article h3,
article h4,
article h5,
article h6,
article img,
section + section h1,
section + section h2,
section + section h3,
section + section h4,
section + section h5,
section + section h6,
section + section img {
page-break-after: avoid;
page-break-inside: avoid; }
article a,
article blockquote,
article table,
article ul,
article ol,
section + section a,
section + section blockquote,
section + section table,
section + section ul,
section + section ol {
page-break-before: avoid;
page-break-inside: avoid; }
article pre,
article dt + dd,
section + section pre,
section + section dt + dd {
page-break-before: avoid; }
article h1 + ul,
article h2 + ul,
article h3 + ul,
article h4 + ul,
article h5 + ul,
article h6 + ul,
article h1 + ol,
article h2 + ol,
article h3 + ol,
article h4 + ol,
article h5 + ol,
article h6 + ol,
section + section h1 + ul,
section + section h2 + ul,
section + section h3 + ul,
section + section h4 + ul,
section + section h5 + ul,
section + section h6 + ul,
section + section h1 + ol,
section + section h2 + ol,
section + section h3 + ol,
section + section h4 + ol,
section + section h5 + ol,
section + section h6 + ol {
page-break-inside: auto; }
article div.admonition,
section + section div.admonition {
page-break-inside: avoid; }
article div.tabbed-content--wrap,
section + section div.tabbed-content--wrap {
page-break-inside: avoid; }
article * + div.highlight,
article * + div.tabbed-set,
section + section * + div.highlight,
section + section * + div.tabbed-set {
page-break-before: avoid; }
section.two-columns {
columns: 2;
column-gap: 1cm; }
article h1 {
border-bottom: 2px solid #f00; }
article h2 {
border-bottom: 1px solid #f99; }
article h3 {
border-bottom: 0.5px solid #eee; }
article h1 > .pdf-order,
article h2 > .pdf-order,
article h3 > .pdf-order {
padding-left: 6px; }
article#doc-toc {
margin: 0 .8rem 1.2rem;
padding-top: .6rem;
font-size: 12pt;
line-height: 1.6; }
article#doc-toc > h1 {
margin: 0 0 2rem;
color: rgba(0, 0, 0, 0.54);
font-size: 1.2rem;
font-weight: 300;
letter-spacing: -.01em;
page-break-after: auto;
page-break-inside: auto;
bookmark-level: none; }
article#doc-toc > h1::before {
display: block;
margin-top: -9px;
padding-top: 9px;
content: ""; }
article#doc-toc ul {
list-style: none;
padding-left: 0;
margin-left: 1em;
font-size: 8pt;
page-break-inside: auto !important; }
article#doc-toc li {
list-style: none; }
article#doc-toc li > a {
color: inherit;
text-decoration: none;
padding-top: 0.4rem;
border-bottom: 1px dotted #ddd;
display: inline-block;
width: 100%; }
article#doc-toc li > a > .pdf-order {
padding-right: 0.5em; }
article#doc-toc li > a::after {
float: right;
content: target-counter(attr(href), page); }
@media print {
html {
font-size: 95%;
font-family: 'Kelmscott Roman NF', 'Noto Serif JP', serif; }
body {
font-size: 10.5pt;
font-family: 'Classico URW T OT', 'Noto Serif JP', serif; }
article {
font-size: 0.8rem;
line-height: 1.6; }
h1,
h2,
h3,
h4,
h5,
h6,
label {
font-family: 'Gentium Book Basic', 'Helvetica Neue', Helvetica, Arial, 'Noto Sans JP', 'Roboto', sans-serif; }
pre,
code,
var,
samp,
kbd,
tt {
font-family: 'Noto Sans Mono CJK JP', 'Courier New', monospace, serif;
font-size: 0.8em; }
pre code,
pre var,
pre samp,
pre kbd,
pre tt {
font-size: 100%; } }
@page :first {
@top-right {
content: normal; }
@bottom-center {
content: normal; }
@bottom-right {
content: normal; } }
article#doc-cover {
display: flex;
flex-direction: column;
height: 100vh; }
article#doc-cover > .wrapper {
flex: 1 0 auto;
text-align: center;
padding: 0 3rem; }
article#doc-cover > .wrapper.upper {
flex: 1 1 auto;
max-height: 30vh; }
article#doc-cover > .wrapper.upper > .logo {
width: 100%;
height: 100%;
background-position: center bottom;
background-repeat: no-repeat;
background-size: contain; }
article#doc-cover > .wrapper h1 {
font-size: 36pt;
line-height: 1.4; }
article#doc-cover > .wrapper h2 {
border: none;
bookmark-level: none; }
article#doc-cover > .properties {
flex: 0 0 auto;
text-align: right;
padding: 0 2rem; }
article#doc-cover > .properties p#copyright {
font-size: 80%; }
article#doc-back_cover {
page: back_cover;
height: 100vh;
display: flex;
flex-direction: column;
text-align: center; }
article#doc-back_cover .wrapper {
flex: 1 0 auto;
display: inline-block;
background: #eee; }
article#doc-back_cover .qrcode {
display: inline-block;
flex: 0 0 auto;
background: #eee; }
article#doc-back_cover .qrcode > a {
display: inline-block;
padding: 0.5em 2em;
border-radius: 1em;
background-color: #fff; }
article#doc-back_cover .qrcode > a img {
width: 100%;
height: auto; }
@page back_cover {
background-color: #eee;
@top-right {
content: normal; }
@bottom-center {
content: normal; }
@bottom-left {
font-size: 8pt;
content: string(title); }
@bottom-right {
font-size: 8pt;
content: string(copyright); } }
@media print {
html {
font-size: 95%;
font-family: 'Ubuntu'; }
body {
font-size: 11.5pt;
font-family: 'Ubuntu'; }
article {
font-size: 0.8rem;
line-height: 1.6; }
h1,
h2,
h3,
h4,
h5,
h6,
label {
font-family: 'Gentium Book Basic', 'Helvetica Neue', Helvetica, Arial, 'Noto Sans JP', 'Roboto', sans-serif; }
pre,
code,
var,
samp,
kbd,
tt {
font-family: 'Noto Sans Mono CJK JP', 'Courier New', monospace, serif;
font-size: 0.8em; }
pre code,
pre var,
pre samp,
pre kbd,
pre tt {
font-size: 100%; } }
</style><style>@media print {
.md-typeset .tabbed-set {
display: block; }
.md-typeset .tabbed-set .tabbed-content--wrap {
display: inline-block;
width: 100%;
margin-bottom: 0.25rem; }
.md-typeset .tabbed-set .tabbed-content--wrap > input {
display: none; }
.md-typeset .tabbed-set .tabbed-content--wrap > label {
background-color: rgba(170, 170, 170, 0.2);
padding: 0.2rem 2rem;
margin: 0;
font-weight: 600;
color: #666;
border-radius: 3px 3px 0 0;
border-bottom-color: transparent; }
.md-typeset .tabbed-set .tabbed-content--wrap > .tabbed-content {
display: block;
box-shadow: none; }
.md-typeset .admonition-title + .tabbed-set:last-child,
.md-typeset summary + .tabbed-set:last-child {
margin-top: 0.5rem; }
.md-typeset > details summary::after {
display: none; }
.md-typeset > div, .md-typeset > div.admonition, .md-typeset > details, .md-typeset > div.tabbed-set, .md-typeset > blockquote, .md-typeset > mark, .md-typeset > table, .md-typeset > p {
margin-left: 0.7rem; }
.md-typeset > table.highlighttable {
margin: 0 0 0 0.7rem; }
.md-typeset table.highlighttable,
.md-typeset table.highlighttable > tbody {
width: 100%; }
.md-typeset table.highlighttable {
padding-bottom: 0.5em; }
.md-typeset table.highlighttable .linenos {
padding-bottom: 0; }
.md-typeset > ul {
margin-left: 1.5rem; }
.md-typeset h4,
.md-typeset h5,
.md-typeset h6 {
margin-left: 0.25rem; }
.md-typeset *:not(h3) + h4 {
margin-top: 1.5rem; }
.md-typeset hr {
border-bottom: 1px dotted #999; }
.md-typeset .footnote {
line-height: 1.1; }
.md-typeset table:not([class]) th, .md-typeset table:not([class]) td {
padding: 0.5em 1.25rem 0.4em; }
.md-typeset table:not([class]) td {
border-bottom: 1px solid #eee; }
.md-typeset span.twemoji {
vertical-align: middle;
height: 1.125rem;
display: inline-block; }
.md-typeset span.twemoji > img {
height: 1.125rem;
vertical-align: middle;
display: inline-block; } }
.md-typeset kbd {
vertical-align: middle;
border-color: #b8b8b8;
border-style: outset;
border-width: .05rem .075rem .1rem .075rem;
border-radius: .25rem;
box-shadow: none; }
:root {
--print-md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23448aff"><path d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z"/></svg>');
--print-md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2300b0ff"><path d="M4 5h16v2H4V5m0 4h16v2H4V9m0 4h16v2H4v-2m0 4h10v2H4v-2z"/></svg>');
--print-md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2300b8d4"><path d="M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10A10 10 0 0012 2z"/></svg>');
--print-md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2300bfa5"><path d="M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 01-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z"/></svg>');
--print-md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2300c853"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
--print-md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2364dd17"><path d="M15.07 11.25l-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 00-2-2 2 2 0 00-2 2H8a4 4 0 014-4 4 4 0 014 4 3.2 3.2 0 01-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10c0-5.53-4.5-10-10-10z"/></svg>');
--print-md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23ff9100"><path d="M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z"/></svg>');
--print-md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23ff5252"><path d="M12 2c5.53 0 10 4.47 10 10s-4.47 10-10 10S2 17.53 2 12 6.47 2 12 2m3.59 5L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41 15.59 7z"/></svg>');
--print-md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23ff1744"><path d="M11.5 20l4.86-9.73H13V4l-5 9.73h3.5V20M12 2c2.75 0 5.1 1 7.05 2.95C21 6.9 22 9.25 22 12s-1 5.1-2.95 7.05C17.1 21 14.75 22 12 22s-5.1-1-7.05-2.95C3 17.1 2 14.75 2 12s1-5.1 2.95-7.05C6.9 3 9.25 2 12 2z"/></svg>');
--print-md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23f50057"><path d="M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 00-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 00-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z"/></svg>');
--print-md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23651fff"><path d="M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 01.75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z"/></svg>');
--print-md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239e9e9e"><path d="M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z"/></svg>'); }
@media print {
.md-typeset .admonition-title {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .admonition-title::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--note); }
.md-typeset .note > .admonition-title, .md-typeset .note > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .note > .admonition-title::before, .md-typeset .note > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--note); }
.md-typeset .abstract > .admonition-title, .md-typeset .tldr > .admonition-title, .md-typeset .summary > .admonition-title, .md-typeset .abstract > summary, .md-typeset .tldr > summary, .md-typeset .summary > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .abstract > .admonition-title::before, .md-typeset .tldr > .admonition-title::before, .md-typeset .summary > .admonition-title::before, .md-typeset .abstract > summary::before, .md-typeset .tldr > summary::before, .md-typeset .summary > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--abstract); }
.md-typeset .info > .admonition-title, .md-typeset .todo > .admonition-title, .md-typeset .info > summary, .md-typeset .todo > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .info > .admonition-title::before, .md-typeset .todo > .admonition-title::before, .md-typeset .info > summary::before, .md-typeset .todo > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--info); }
.md-typeset .tip > .admonition-title, .md-typeset .important > .admonition-title, .md-typeset .hint > .admonition-title, .md-typeset .tip > summary, .md-typeset .important > summary, .md-typeset .hint > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .tip > .admonition-title::before, .md-typeset .important > .admonition-title::before, .md-typeset .hint > .admonition-title::before, .md-typeset .tip > summary::before, .md-typeset .important > summary::before, .md-typeset .hint > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--tip); }
.md-typeset .success > .admonition-title, .md-typeset .done > .admonition-title, .md-typeset .check > .admonition-title, .md-typeset .success > summary, .md-typeset .done > summary, .md-typeset .check > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .success > .admonition-title::before, .md-typeset .done > .admonition-title::before, .md-typeset .check > .admonition-title::before, .md-typeset .success > summary::before, .md-typeset .done > summary::before, .md-typeset .check > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--success); }
.md-typeset .question > .admonition-title, .md-typeset .faq > .admonition-title, .md-typeset .help > .admonition-title, .md-typeset .question > summary, .md-typeset .faq > summary, .md-typeset .help > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .question > .admonition-title::before, .md-typeset .faq > .admonition-title::before, .md-typeset .help > .admonition-title::before, .md-typeset .question > summary::before, .md-typeset .faq > summary::before, .md-typeset .help > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--question); }
.md-typeset .warning > .admonition-title, .md-typeset .attention > .admonition-title, .md-typeset .caution > .admonition-title, .md-typeset .warning > summary, .md-typeset .attention > summary, .md-typeset .caution > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .warning > .admonition-title::before, .md-typeset .attention > .admonition-title::before, .md-typeset .caution > .admonition-title::before, .md-typeset .warning > summary::before, .md-typeset .attention > summary::before, .md-typeset .caution > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--warning); }
.md-typeset .failure > .admonition-title, .md-typeset .missing > .admonition-title, .md-typeset .fail > .admonition-title, .md-typeset .failure > summary, .md-typeset .missing > summary, .md-typeset .fail > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .failure > .admonition-title::before, .md-typeset .missing > .admonition-title::before, .md-typeset .fail > .admonition-title::before, .md-typeset .failure > summary::before, .md-typeset .missing > summary::before, .md-typeset .fail > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--failure); }
.md-typeset .danger > .admonition-title, .md-typeset .error > .admonition-title, .md-typeset .danger > summary, .md-typeset .error > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .danger > .admonition-title::before, .md-typeset .error > .admonition-title::before, .md-typeset .danger > summary::before, .md-typeset .error > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--danger); }
.md-typeset .bug > .admonition-title, .md-typeset .bug > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .bug > .admonition-title::before, .md-typeset .bug > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--bug); }
.md-typeset .example > .admonition-title, .md-typeset .example > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .example > .admonition-title::before, .md-typeset .example > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--example); }
.md-typeset .quote > .admonition-title, .md-typeset .cite > .admonition-title, .md-typeset .quote > summary, .md-typeset .cite > summary {
padding: .6rem .6rem .4rem 2.2rem; }
.md-typeset .quote > .admonition-title::before, .md-typeset .cite > .admonition-title::before, .md-typeset .quote > summary::before, .md-typeset .cite > summary::before {
margin-top: -0.15rem;
background-color: transparent;
background-image: var(--print-md-admonition-icon--quote); } }
:root {
--print-md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23000000de"><path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.42L5.83 13H21V7h-2z"/></svg>'); }
@media print {
.md-typeset .footnote-backref::before {
background-color: transparent;
background-image: var(--print-md-footnotes-icon); } }
:root {
--print-md-details-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="hsla(0, 0%, 0%, 0.87)"><path d="M8.59 16.58L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42z"/></svg>'); }
@media print {
.md-typeset summary::after {
background-color: transparent;
background-image: var(--print-md-details-icon);
-webkit-mask-image: none;
mask-image: none; } }
:root {
--print-md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23ddd"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
--print-md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%2300e676"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>'); }
@media print {
.md-typeset .task-list-control .task-list-indicator::before {
background-color: transparent;
background-image: var(--print-md-tasklist-icon); }
.md-typeset .task-list-control [type=checkbox]:checked + .task-list-indicator::before {
background-color: transparent;
background-image: var(--print-md-tasklist-icon--checked); } }
/*# sourceMappingURL=material-polyfills.css.map*/
</style></head><body>I am a cover!<article id="doc-toc"><h1>Table of contents</h1><ul><li><a href="#.:lets-build-your-awesome-selfhosted-platform-together">Let's build your awesome selfhosted platform together!</a><ul><li><a href="#.:what-to-expect">What to expect</a></li><li><a href="#.:how-will-this-benefit-me">How will this benefit me?</a></li><li><a href="#.:testimonials">Testimonials</a></li><li><a href="#.:who-made-this">Who made this?</a><ul><li><a href="#.:hi-im-david">👋 Hi, I'm David</a></li><li><a href="#.:what-do-you-want-from-me">What do you want from me?</a></li><li><a href="#.:sponsored-projects">Sponsored Projects</a></li></ul></li></ul></li><li><a href="#IRXWG23FOIQFG53BOJWQ/">Docker Swarm</a><ul><li><a href="#docker-swarm/:highly-available-docker-swarm-design">Highly Available Docker Swarm Design</a><ul><li><a href="#docker-swarm/:design-decisions">Design Decisions</a></li><li><a href="#docker-swarm/:security">Security</a></li><li><a href="#docker-swarm/:high-availability">High availability</a></li></ul></li><li><a href="#IRXWG23FOIQFG53BOJWQ/KBZGK4DBOJQXI2LPNY/">Preparation</a><ul><li><a href="#docker-swarm/design/:highly-available-docker-swarm-design">Highly Available Docker Swarm Design</a></li><li><a href="#docker-swarm/nodes/:nodes">Nodes</a></li></ul></li></ul></li><li><a href="#4KN3IICLOVRGK4TOMV2GK4Y/">⛴ Kubernetes</a><ul><li><a href="#4KN3IICLOVRGK4TOMV2GK4Y/KBZGK4DBOJQXI2LPNY/">Preparation</a><ul><li><a href="#kubernetes/">Introduction</a></li></ul></li></ul></li></ul></article><article class="md-content__inner md-typeset" data-url="/." id=".:"> <h1 id=".:lets-build-your-awesome-selfhosted-platform-together">Let's build your awesome selfhosted platform together!</h1> <p>Welcome, fellow geek <img alt="👋" class="twemoji" src="https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg" title=":wave:"/> If you're impatient, just start here <img alt="👇" class="twemoji" src="https://twemoji.maxcdn.com/v/latest/svg/1f447.svg" title=":point_down:"/></p> <div class="grid cards"> <ul> <li> <p><strong>Dive into <span class="twemoji docker lg middle"><img class="converted-twemoji" src="data:image/svg+xml;charset=utf-8;base64,PHN2ZyBoZWlnaHQ9IjI0IiBzdHlsZT0iZmlsbDogY3VycmVudENvbG9yOyIgdmlld2JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIxLjgxIDEwLjI1Yy0uMDYtLjA0LS41Ni0uNDMtMS42NC0uNDMtLjI4IDAtLjU2LjAzLS44NC4wOC0uMjEtMS40LTEuMzgtMi4xMS0xLjQzLTIuMTRsLS4yOS0uMTctLjE4LjI3Yy0uMjQuMzYtLjQzLjc3LS41MSAxLjE5LS4yLjgtLjA4IDEuNTYuMzMgMi4yMS0uNDkuMjgtMS4yOS4zNS0xLjQ2LjM1SDIuNjJjLS4zNCAwLS42Mi4yOC0uNjIuNjMgMCAxLjE1LjE4IDIuMy41OCAzLjM4LjQ1IDEuMTkgMS4xMyAyLjA3IDIgMi42MS45OC42IDIuNTkuOTQgNC40Mi45NC43OSAwIDEuNjEtLjA3IDIuNDItLjIyIDEuMTItLjIgMi4yLS41OSAzLjE5LTEuMTZBOC4zIDguMyAwIDAgMCAxNi43OCAxNmMxLjA1LTEuMTcgMS42Ny0yLjUgMi4xMi0zLjY1aC4xOWMxLjE0IDAgMS44NS0uNDYgMi4yNC0uODUuMjYtLjI0LjQ1LS41My41OS0uODdsLjA4LS4yNC0uMTktLjE0bS0xNy45Ni45OWgxLjc2Yy4wOCAwIC4xNi0uMDcuMTYtLjE2VjkuNWMwLS4wOC0uMDctLjE2LS4xNi0uMTZIMy44NWMtLjA5IDAtLjE2LjA3LS4xNi4xNnYxLjU4Yy4wMS4wOS4wNy4xNi4xNi4xNm0yLjQzIDBoMS43NmMuMDggMCAuMTYtLjA3LjE2LS4xNlY5LjVjMC0uMDgtLjA3LS4xNi0uMTYtLjE2SDYuMjhjLS4wOSAwLS4xNi4wNy0uMTYuMTZ2MS41OGMuMDEuMDkuMDcuMTYuMTYuMTZtMi40NyAwaDEuNzVjLjEgMCAuMTctLjA3LjE3LS4xNlY5LjVjMC0uMDgtLjA2LS4xNi0uMTctLjE2SDguNzVjLS4wOCAwLS4xNS4wNy0uMTUuMTZ2MS41OGMwIC4wOS4wNi4xNi4xNS4xNm0yLjQ0IDBoMS43N2MuMDggMCAuMTUtLjA3LjE1LS4xNlY5LjVjMC0uMDgtLjA2LS4xNi0uMTUtLjE2aC0xLjc3Yy0uMDggMC0uMTUuMDctLjE1LjE2djEuNThjMCAuMDkuMDcuMTYuMTUuMTZNNi4yOCA5aDEuNzZjLjA4IDAgLjE2LS4wOS4xNi0uMThWNy4yNWMwLS4wOS0uMDctLjE2LS4xNi0uMTZINi4yOGMtLjA5IDAtLjE2LjA2LS4xNi4xNnYxLjU3Yy4wMS4wOS4wNy4xOC4xNi4xOG0yLjQ3IDBoMS43NWMuMSAwIC4xNy0uMDkuMTctLjE4VjcuMjVjMC0uMDktLjA2LS4xNi0uMTctLjE2SDguNzVjLS4wOCAwLS4xNS4wNi0uMTUuMTZ2MS41N2MwIC4wOS4wNi4xOC4xNS4xOG0yLjQ0IDBoMS43N2MuMDggMCAuMTUtLjA5LjE1LS4xOFY3LjI1YzAtLjA5LS4wNy0uMTYtLjE1LS4xNmgtMS43N2MtLjA4IDAtLjE1LjA2LS4xNS4xNnYxLjU3YzAgLjA5LjA3LjE4LjE1LjE4bTAtMi4yOGgxLjc3Yy4wOCAwIC4xNS0uMDcuMTUtLjE2VjVjMC0uMS0uMDctLjE3LS4xNS0uMTdoLTEuNzdjLS4wOCAwLS4xNS4wNi0uMTUuMTd2MS41NmMwIC4wOC4wNy4xNi4xNS4xNm0yLjQ2IDQuNTJoMS43NmMuMDkgMCAuMTYtLjA3LjE2LS4xNlY5LjVjMC0uMDgtLjA3LS4xNi0uMTYtLjE2aC0xLjc2Yy0uMDggMC0uMTUuMDctLjE1LjE2djEuNThjMCAuMDkuMDcuMTYuMTUuMTYiPjwvcGF0aD48L3N2Zz4="/></span> <a href="/docker-swarm/design/">Docker Swarm</a></strong></p> <hr/> <p>The quickest way to get started, and to get your head around the basics.</p> </li> <li> <p><strong>Kick it with <span class="twemoji kubernetes lg middle"><img class="converted-twemoji" src="data:image/svg+xml;charset=utf-8;base64,PHN2ZyBoZWlnaHQ9IjI0IiBzdHlsZT0iZmlsbDogY3VycmVudENvbG9yOyIgdmlld2JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEzLjk1IDEzLjVoLS4yM2MtLjE4LjExLS4yNi4zMi0uMTguNWwuODYgMi4xMWMuODMtLjUzIDEuNDYtMS4zMiAxLjc5LTIuMjVsLTIuMjMtLjM2aC0uMDFtLTMuNDUuMjlhLjQxNS40MTUgMCAwIDAtLjM4LS4yOWgtLjA4bC0yLjIyLjM3Yy4zMy45Mi45NiAxLjcgMS43OSAyLjIzbC44NS0yLjA3VjE0Yy4wNC0uMDUuMDQtLjE0LjA0LS4yMW0xLjgzLjgxYS4zNzguMLine truncated
-A INPUT -s <span class="m">192</span>.168.31.0/24 -j ACCEPT
</code></pre></div> <p>And restart iptables with <code>systemctl restart iptables</code></p> <h6 id="docker-swarm/nodes/:ubuntu">Ubuntu</h6> <p>Install the (<em>non-default</em>) persistent iptables tools, by running <code>apt-get install iptables-persistent</code>, establishing some default rules (<em>dkpg will prompt you to save current ruleset</em>), and then add something like this to <code>/etc/iptables/rules.v4</code>:</p> <div class="highlight"><pre><span></span><code><span class="c1"># Allow all inter-node communication</span>
-A INPUT -s <span class="m">192</span>.168.31.0/24 -j ACCEPT
</code></pre></div> <p>And refresh your running iptables rules with <code>iptables-restore &lt; /etc/iptables/rules.v4</code></p> <h5 id="docker-swarm/nodes/:enable-hostname-resolution">Enable hostname resolution</h5> <p>Depending on your hosting environment, you may have DNS automatically setup for your VMs. If not, it's useful to set up static entries in /etc/hosts for the nodes. For example, I setup the following:</p> <ul> <li>192.168.31.11 ds1 ds1.funkypenguin.co.nz</li> <li>192.168.31.12 ds2 ds2.funkypenguin.co.nz</li> <li>192.168.31.13 ds3 ds3.funkypenguin.co.nz</li> </ul> <h5 id="docker-swarm/nodes/:set-timezone">Set timezone</h5> <p>Set your local timezone, by running:</p> <div class="highlight"><pre><span></span><code>ln -sf /usr/share/zoneinfo/&lt;your timezone&gt; /etc/localtime
</code></pre></div> <h4 id="docker-swarm/nodes/:serving">Serving</h4> <p>After completing the above, you should have:</p> <div class="admonition summary"> <p class="admonition-title">Summary</p> <p>Deployed in this recipe:</p> <ul class="task-list"> <li class="task-list-item"><label class="task-list-control"><input checked="" disabled="" type="checkbox"/><span class="task-list-indicator"></span></label> 3 x nodes (<em>bare-metal or VMs</em>), each with:<ul> <li>A mainstream Linux OS (<em>tested on either <a href="https://www.centos.org">CentOS</a> 7+ or <a href="http://releases.ubuntu.com">Ubuntu</a> 16.04+</em>)</li> <li>At least 2GB RAM</li> <li>At least 20GB disk space (<em>but it'll be tight</em>)</li> </ul> </li> <li class="task-list-item"><label class="task-list-control"><input checked="" disabled="" type="checkbox"/><span class="task-list-indicator"></span></label> Connectivity to each other within the same subnet, and on a low-latency link (<em>i.e., no WAN links</em>)</li> </ul> </div> <hr/> <div class="md-source-file"> <small> Last update: <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">July 9, 2022</span> </small> </div> </section></section></article><article class="md-content__inner md-typeset" data-url="/4KN3IICLOVRGK4TOMV2GK4Y/" id="4KN3IICLOVRGK4TOMV2GK4Y/:"><h1 id="4KN3IICLOVRGK4TOMV2GK4Y/">⛴ Kubernetes</h1><section class="md-typeset" data-url="/4KN3IICLOVRGK4TOMV2GK4Y/KBZGK4DBOJQXI2LPNY/" id="4KN3IICLOVRGK4TOMV2GK4Y/KBZGK4DBOJQXI2LPNY/:"><h2 id="4KN3IICLOVRGK4TOMV2GK4Y/KBZGK4DBOJQXI2LPNY/">Preparation</h2><section class="md-typeset" data-url="/kubernetes/" id="kubernetes/:"> <h3 id="kubernetes/">Introduction</h3> <p>My first introduction to Kubernetes was a children's story:</p> <!-- markdownlint-disable MD033 --> <iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/R9-SOzep73w" width="560"></iframe> <h4 id="kubernetes/:why-kubernetes">Why Kubernetes?</h4> <p>Why would you want to Kubernetes for your self-hosted recipes, over simple <a href="/docker-swarm/">Docker Swarm</a>? Here's my personal take..</p> <h5 id="kubernetes/:docker-swarm-is-dead">Docker Swarm is dead</h5> <p>Sorry to say, but from where I sit, there's no innovation or development happening in docker swarm.</p> <p>Yes, I know, after Docker Inc <a href="https://www.mirantis.com/blog/mirantis-acquires-docker-enterprise-platform-business/">sold its platform business to Mirantis in Nov 2019</a>, in Feb 2020 Mirantis <a href="https://www.mirantis.com/blog/mirantis-will-continue-to-support-and-develop-docker-swarm/">back-tracked</a> on their original plan to sunset swarm after 2 years, and stated that they'd continue to invest in swarm. But seriously, look around. Nobody is interested in swarm right now...</p> <p>... Not even Mirantis! As of Nov 2021, the Mirantis blog tag "<a href="https://www.mirantis.com/tag/kubernetes/">kubernetes</a>" had 8 posts within the past month. The tag "<a href="https://www.mirantis.com/tag/docker/">docker</a>" has 8 posts in the past <strong>2 years</strong>, the 8<sup>th</sup> being the original announcement of the Docker aquisition. The tag "<a href="https://www.mirantis.com/tag/docker-swarm/">docker swarm</a>" has only 2 posts, <strong>ever</strong>.</p> <p>Dead. <a href="https://youtu.be/NxnZC9L_YXE?t=47">Extinct. Like the doodoo</a>.</p> <h5 id="kubernetes/:once-you-go-kubernetes-you-cant-go-back">Once you go Kubernetes, you can't go back</h5> <p>For years now, <a href="https://www.funkypenguin.co.nz/work-with-me/">I've provided Kubernetes design consulting</a> to small clients and large enterprises. The implementation details in each case vary widely, but there are some primitives which I've come to take for granted, and I wouldn't easily do without. A few examples:</p> <ul> <li><strong>CLI drives API from anywhere</strong>. From my laptop, I can use my credentials to manage any number of Kubernetes clusters, simply by switching kubectl "context". Each interaction is an API call against an HTTPS endpoint. No SSHing to hosts and manually running docker command as root!</li> <li><strong>GitOps is magic</strong>. There are multiple ways to achieve it, but having changes you commit to a repo automatically applied to a cluster, "Just Works™". The process removes so much friction from making changes that it makes you more productive, and a better "gitizen" ;P</li> <li><strong>Controllers are trustworthy</strong>. I've come to trust that when I tell Kubernetes to run 3 replicas on separate hosts, to scale up a set of replicas based on CPU load metrics, or provision a blob of storage for a given workloa, that this will be done in a consistent and visible way. I'll be able to see logs / details for each action taken by the controller, and adjust my own instructions/configuration accordingly if necessary.</li> </ul> <h4 id="kubernetes/:uggh-itLine truncated
<div class="wrapper"></div><div class="qrcode">
<a href="https://geek-cookbook.funkypenguin.co.nz/">
<img src="data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4KPHN2ZyB3aWR0aD0iMzdtbSIgaGVpZ2h0PSIzN21tIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzNyAzNyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNIDMwIDI1IEwgMzAgMjYgTCAzMSAyNiBMIDMxIDI1IHogTSAyMyA0IEwgMjMgNSBMIDI0IDUgTCAyNCA0IHogTSAxMiAxMyBMIDEyIDE0IEwgMTMgMTQgTCAxMyAxMyB6IE0gMjMgMTMgTCAyMyAxNCBMIDI0IDE0IEwgMjQgMTMgeiBNIDQgOSBMIDQgMTAgTCA1IDEwIEwgNSA5IHogTSAxNSA5IEwgMTUgMTAgTCAxNiAxMCBMIDE2IDkgeiBNIDQgMjcgTCA0IDI4IEwgNSAyOCBMIDUgMjcgeiBNIDE1IDI3IEwgMTUgMjggTCAxNiAyOCBMIDE2IDI3IHogTSAxNCAzMSBMIDE0IDMyIEwgMTUgMzIgTCAxNSAzMSB6IE0gMjkgMjkgTCAyOSAzMCBMIDMwIDMwIEwgMzAgMjkgeiBNIDggNiBMIDggNyBMIDkgNyBMIDkgNiB6IE0gMjYgMTQgTCAyNiAxNSBMIDI3IDE1IEwgMjcgMTQgeiBNIDcgMTAgTCA3IDExIEwgOCAxMSBMIDggMTAgeiBNIDI2IDIzIEwgMjYgMjQgTCAyNyAyNCBMIDI3IDIzIHogTSAyNiAzMiBMIDI2IDMzIEwgMjcgMzMgTCAyNyAzMiB6IE0gNyAyOCBMIDcgMjkgTCA4IDI5IEwgOCAyOCB6IE0gMTggMjggTCAxOCAyOSBMIDE5IDI5IEwgMTkgMjggeiBNIDMwIDIwIEwgMzAgMjEgTCAzMSAyMSBMIDMxIDIwIHogTSAzMCAyOSBMIDMwIDMwIEwgMzEgMzAgTCAzMSAyOSB6IE0gMTIgOCBMIDEyIDkgTCAxMyA5IEwgMTMgOCB6IE0gMjMgOCBMIDIzIDkgTCAyNCA5IEwgMjQgOCB6IE0gNCA0IEwgNCA1IEwgNSA1IEwgNSA0IHogTSA0IDEzIEwgNCAxNCBMIDUgMTQgTCA1IDEzIHogTSAyOSAxNSBMIDI5IDE2IEwgMzAgMTYgTCAzMCAxNSB6IE0gMjYgOSBMIDI2IDEwIEwgMjcgMTAgTCAyNyA5IHogTSAxOCA1IEwgMTggNiBMIDE5IDYgTCAxOSA1IHogTSA3IDE0IEwgNyAxNSBMIDggMTUgTCA4IDE0IHogTSAxOCAxNCBMIDE4IDE1IEwgMTkgMTUgTCAxOSAxNCB6IE0gNyAyMyBMIDcgMjQgTCA4IDI0IEwgOCAyMyB6IE0gMTggMjMgTCAxOCAyNCBMIDE5IDI0IEwgMTkgMjMgeiBNIDcgMzIgTCA3IDMzIEwgOCAzMyBMIDggMzIgeiBNIDE4IDMyIEwgMTggMzMgTCAxOSAzMyBMIDE5IDMyIHogTSAzMCA2IEwgMzAgNyBMIDMxIDcgTCAzMSA2IHogTSAzMCAyNCBMIDMwIDI1IEwgMzEgMjUgTCAzMSAyNCB6IE0gMTUgOCBMIDE1IDkgTCAxNiA5IEwgMTYgOCB6IE0gMjkgMTAgTCAyOSAxMSBMIDMwIDExIEwgMzAgMTAgeiBNIDE0IDIxIEwgMTQgMjIgTCAxNSAyMiBMIDE1IDIxIHogTSAxNCAzMCBMIDE0IDMxIEwgMTUgMzEgTCAxNSAzMCB6IE0gNiAyNiBMIDYgMjcgTCA3IDI3IEwgNyAyNiB6IE0gMjYgNCBMIDI2IDUgTCAyNyA1IEwgMjcgNCB6IE0gMjYgMTMgTCAyNiAxNCBMIDI3IDE0IEwgMjcgMTMgeiBNIDE4IDkgTCAxOCAxMCBMIDE5IDEwIEwgMTkgOSB6IE0gMjYgMjIgTCAyNiAyMyBMIDI3IDIzIEwgMjcgMjIgeiBNIDcgMTggTCA3IDE5IEwgOCAxOSBMIDggMTggeiBNIDI2IDMxIEwgMjYgMzIgTCAyNyAzMiBMIDI3IDMxIHogTSAzMCAxMCBMIDMwIDExIEwgMzEgMTEgTCAzMSAxMCB6IE0gMzIgMjkgTCAzMiAzMCBMIDMzIDMwIEwgMzMgMjkgeiBNIDIyIDYgTCAyMiA3IEwgMjMgNyBMIDIzIDYgeiBNIDExIDE1IEwgMTEgMTYgTCAxMiAxNiBMIDEyIDE1IHogTSAyMiAxNSBMIDIyIDE2IEwgMjMgMTYgTCAyMyAxNSB6IE0gMjUgMTcgTCAyNSAxOCBMIDI2IDE4IEwgMjYgMTcgeiBNIDEwIDI4IEwgMTAgMjkgTCAxMSAyOSBMIDExIDI4IHogTSAxNCA3IEwgMTQgOCBMIDE1IDggTCAxNSA3IHogTSA2IDEyIEwgNiAxMyBMIDcgMTMgTCA3IDEyIHogTSA2IDMwIEwgNiAzMSBMIDcgMzEgTCA3IDMwIHogTSAyNiA4IEwgMjYgOSBMIDI3IDkgTCAyNyA4IHogTSA3IDQgTCA3IDUgTCA4IDUgTCA4IDQgeiBNIDI2IDE3IEwgMjYgMTggTCAyNyAxOCBMIDI3IDE3IHogTSAxOCAyMiBMIDE4IDIzIEwgMTkgMjMgTCAxOSAyMiB6IE0gMjEgMjQgTCAyMSAyNSBMIDIyIDI1IEwgMjIgMjQgeiBNIDMyIDI0IEwgMzIgMjUgTCAzMyAyNSBMIDMzIDI0IHogTSAxMyAyOSBMIDEzIDMwIEwgMTQgMzAgTCAxNCAyOSB6IE0gMTAgMTQgTCAxMCAxNSBMIDExIDE1IEwgMTEgMTQgeiBNIDI1IDIxIEwgMjUgMjIgTCAyNiAyMiBMIDI2IDIxIHogTSAxMCAzMiBMIDEwIDMzIEwgMTEgMzMgTCAxMSAzMiB6IE0gMjUgMzAgTCAyNSAzMSBMIDI2IDMxIEwgMjYgMzAgeiBNIDE0IDExIEwgMTQgMTIgTCAxNSAxMiBMIDE1IDExIHogTSA2IDcgTCA2IDggTCA3IDggTCA3IDcgeiBNIDYgMTYgTCA2IDE3IEwgNyAxNyBMIDcgMTYgeiBNIDI5IDE4IEwgMjkgMTkgTCAzMCAxOSBMIDMwIDE4IHogTSAyNiAxMiBMIDI2IDEzIEwgMjcgMTMgTCAyNyAxMiB6IE0gMTcgMzEgTCAxNyAzMiBMIDE4IDMyIEwgMTggMzEgeiBNIDcgOCBMIDcgOSBMIDggOSBMIDggOCB6IE0gMzIgMTAgTCAzMiAxMSBMIDMzIDExIEwgMzMgMTAgeiBNIDEzIDE1IEwgMTMgMTYgTCAxNCAxNiBMIDE0IDE1IHogTSA5IDI2IEwgOSAyNyBMIDEwIDI3IEwgMTAgMjYgeiBNIDIxIDI4IEwgMjEgMjkgTCAyMiAyOSBMIDIyIDI4IHogTSAzMiAyOCBMIDMyIDI5IEwgMzMgMjkgTCAzMyAyOCB6IE0gMTAgOSBMIDEwIDEwIEwgMTEgMTAgTCAxMSA5IHogTSAxMCAxOCBMIDEwIDE5IEwgMTEgMTkgTCAxMSAxOCB6IE0gMTAgMjcgTCAxMCAyOCBMIDExIDI4IEwgMTEgMjcgeiBNIDI5IDQgTCAyOSA1IEwgMzAgNSBMIDMwIDQgeiBNIDE0IDE1IEwgMTQgMTYgTCAxNSAxNiBMIDE1IDE1IHogTSAxNyAxNyBMIDE3IDE4IEwgMTggMTggTCAxOCAxNyB6IE0gMjggMTcgTCAyOCAxOCBMIDI5IDE4IEwgMjkgMTcgeiBNIDE3IDI2IEwgMTcgMjcgTCAxOCAyNyBMIDE4IDI2IHogTSAyOCAyNiBMIDI4IDI3IEwgMjkgMjcgTCAyOSAyNiB6IE0gMjAgMjIgTCAyMCAyMyBMIDIxIDIzIEwgMjEgMjIgeiBNIDIwIDMxIEwgMjAgMzIgTCAyMSAzMiBMIDIxIDMxIHogTSAyMSA1IEwgMjEgNiBMIDIyIDYgTCAyMiA1IHogTSAzMiA1IEwgMzIgNiBMIDMzIDYgTCAzMyA1IHogTSA5IDEyIEwgOSAxMyBMIDEwIDEzIEwgMTAgMTIgeiBNIDIxIDE0IEwgMjEgMTUgTCAyMiAxNSBMIDIyIDE0IHogTSA5IDIxIEwgOSAyMiBMIDEwIDIyIEwgMTAgMjEgeiBNIDEzIDE5IEwgMTMgMjAgTCAxNCAyMCBMIDE0IDE5IHogTSAyMSAzMiBMIDIxIDMzIEwgMjIgMzMgTCAyMiAzMiB6IE0gMTAgNCBMIDEwIDUgTCAxMSA1IEwgMTEgNCB6IE0gMTAgMjIgTCAxMCAyMyBMIDExIDIzIEwgMTEgMjIgeiBNIDI0IDI0IEwgMjQgMjUgTCAyNSAyNSBMIDI1IDI0IHogTSAxNCAxMCBMIDE0IDExIEwgMTUgMTEgTCAxNSAxMCB6IE0gMTYgMjkgTCAxNiAzMCBMIDE3IDMwIEwgMTcgMjkgeiBNIDYgNiBMIDYgNyBMIDcgNyBMIDcgNiB6IE0gNiAxNSBMIDYgMTYgTCA3IDE2IEwgNyAxNSB6IE0gMTcgMTIgTCAxNyAxMyBMIDE4IDEzIEwgMTggMTIgeiBNIDI4IDEyIEwgMjggMTMgTCAyOSAxMyBMIDI5IDEyIHogTSA1IDE5IEwgNSAyMCBMIDYgMjAgTCA2IDE5IHogTSAxNyAyMSBMIDE3IDIyIEwgMTggMjIgTCAxOCAyMSB6IE0gMjggMjEgTCAyOCAyMiBMIDI5IDIyIEwgMjkgMjEgeiBNIDIwIDE3IEwgMjAgMTggTCAyMSAxOCBMIDIxIDE3IHogTSAxNyAzMCBMIDE3IDMxIEwgMTggMzEgTCAxOCAzMCB6IE0gMLine truncated
<div>https://geek-cookbook.funkypenguin.co.nz/</div>
</a>
</div> <div class="wrapper"></div>
</article></body></html>
Submodule
+1
Submodule sample added at ccf20e5a12.
+2 -2
View File
@@ -2,9 +2,9 @@
set -e set -e
# docker build --build-arg FROM_SOURCE=ghcr.io/geek-cookbook/mkdocs-material-insiders . -t funkypenguin/mkdocs-material --platform amd64 # docker build --build-arg FROM_SOURCE=ghcr.io/geek-cookbook/mkdocs-material-insiders . -t funkypenguin/mkdocs-material --platform amd64
# Prepare slimmed-down versions for PDFing # Prepare slimmed-down versions for PDFing (replace gsed with sed for linux)
cp -rf manuscript docs_to_pdf cp -rf manuscript docs_to_pdf
find docs_to_pdf -type f -exec sed -i -e 's/recipe-footer.md/common-links.md/g' {} \; find docs_to_pdf -type f -exec gsed -i -e 's/recipe-footer.md/common-links.md/g' {} \;
# Build PDF from slimmed recipes # Build PDF from slimmed recipes
docker run --rm --name mkdocs-material -e ENABLE_PDF_EXPORT=1 -v ${PWD}/docs_to_pdf:/docs funkypenguin/mkdocs-material build -f mkdocs-pdf-print.yml docker run --rm --name mkdocs-material -e ENABLE_PDF_EXPORT=1 -v ${PWD}/docs_to_pdf:/docs funkypenguin/mkdocs-material build -f mkdocs-pdf-print.yml
+1
View File
@@ -0,0 +1 @@
I am a cover!
+45
View File
@@ -0,0 +1,45 @@
@media print {
html {
font-size: 95%;
font-family: 'Ubuntu';
}
body {
font-size: 11.5pt;
font-family: 'Ubuntu';
}
article {
font-size: 0.8rem;
line-height: 1.6;
}
h1,
h2,
h3,
h4,
h5,
h6,
label {
font-family: 'Gentium Book Basic', 'Helvetica Neue', Helvetica, Arial, 'Noto Sans JP', 'Roboto', sans-serif;
}
pre,
code,
var,
samp,
kbd,
tt {
font-family: 'Noto Sans Mono CJK JP', 'Courier New', monospace, serif;
font-size: 0.8em;
}
pre code,
pre var,
pre samp,
pre kbd,
pre tt {
font-size: 100%;
}
}