├── .gitignore ├── .gitmodules ├── 2024-05-pikvm ├── README.md └── tesmart-kvmd-override.yaml ├── 2024-06-subnet-routers └── README.md ├── 2024-07-cloud-init ├── 01-cloud-init-do-manual │ └── README.md └── 02-cloud-init-do-terraform │ ├── ansible │ ├── ansible.cfg │ ├── group_vars │ │ └── caddy.yaml │ ├── hosts.ini │ ├── requirements.yaml │ ├── run.yaml │ └── vars │ │ └── secrets.yaml │ ├── compose.yaml │ ├── justfile │ └── terraform │ ├── README.md │ ├── digitalocean.auto.tfvars.example │ ├── digitalocean.tftpl │ └── main.tf ├── 2024-11-tsdproxy └── compose.yaml ├── 2024-12-tsidp ├── install-tsidp.sh └── tsidp.zip ├── 2025-01-beszel └── beszel-hub │ ├── compose.yaml │ └── config │ └── beszel-hub.json ├── 2025-02-metrics ├── compose.yaml ├── grafana │ └── grafana.json ├── loadgen │ ├── Dockerfile │ ├── README.md │ └── highload.py └── prometheus │ └── prometheus.yml ├── 2025-02-nix-nvidia-ollama ├── ansible │ ├── ansible.cfg │ ├── hosts.ini │ ├── playbook.yaml │ └── services │ │ └── nix-llm │ │ ├── 01-ai.yaml │ │ └── openwebui.json ├── justfile └── nix │ ├── flake.lock │ ├── flake.nix │ ├── hosts │ ├── common │ │ ├── common-packages.nix │ │ └── nixos-common.nix │ └── nixos │ │ └── nix-llm │ │ ├── default.nix │ │ ├── disko.nix │ │ ├── hardware-configuration.nix │ │ └── install-nix.sh │ └── lib │ ├── default.nix │ └── helpers.nix ├── 2025-04-mazanoke ├── compose.yaml └── mazanoke.json ├── 2025-04-pocketid └── compose.yaml ├── 2025-05-karakeep └── compose.yaml ├── 2025-06-self-hosting-part2 ├── audiobookshelf │ ├── audiobookshelf.json │ └── compose.yaml └── immich │ ├── compose.yaml │ └── immich.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | galaxy_roles/ 2 | .vault-password 3 | 4 | # Created by https://www.toptal.com/developers/gitignore/api/terraform 5 | # Edit at https://www.toptal.com/developers/gitignore?templates=terraform 6 | 7 | ### Terraform ### 8 | # Local .terraform directories 9 | **/.terraform/* 10 | 11 | # .tfstate files 12 | *.tfstate 13 | *.tfstate.* 14 | 15 | # Crash log files 16 | crash.log 17 | crash.*.log 18 | 19 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 20 | # password, private keys, and other secrets. These should not be part of version 21 | # control as they are data points which are potentially sensitive and subject 22 | # to change depending on the environment. 23 | *.tfvars 24 | *.tfvars.json 25 | *.lock.hcl 26 | 27 | # Ignore override files as they are usually used to override resources locally and so 28 | # are not checked in 29 | override.tf 30 | override.tf.json 31 | *_override.tf 32 | *_override.tf.json 33 | 34 | # Include override files you do wish to add to version control using negated pattern 35 | # !example_override.tf 36 | 37 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 38 | # example: *tfplan* 39 | 40 | # Ignore CLI configuration files 41 | .terraformrc 42 | terraform.rc 43 | 44 | # End of https://www.toptal.com/developers/gitignore/api/terraform 45 | 46 | # Created by https://www.toptal.com/developers/gitignore/api/macos 47 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos 48 | 49 | ### macOS ### 50 | # General 51 | .DS_Store 52 | .AppleDouble 53 | .LSOverride 54 | 55 | # Icon must end with two \r 56 | Icon 57 | 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | ### macOS Patch ### 79 | # iCloud generated files 80 | *.icloud 81 | 82 | # End of https://www.toptal.com/developers/gitignore/api/macos 83 | 84 | roles/* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "2025-02-nix-nvidia-ollama/ansible/roles/ironicbadger.docker-compose-generator"] 2 | path = 2025-02-nix-nvidia-ollama/ansible/roles/ironicbadger.docker-compose-generator 3 | url = git@github.com:ironicbadger/ansible-role-docker-compose-generator.git -------------------------------------------------------------------------------- /2024-05-pikvm/README.md: -------------------------------------------------------------------------------- 1 | # Install Tailscale on a PiKVM 2 | 3 | https://tailscale.com/blog/remote-access-with-pikvm-and-tailscale 4 | 5 | 6 | [![Install Tailscale on a PiKVM](https://img.youtube.com/vi/Btqw56DFhro/maxresdefault.jpg)](https://www.youtube.com/embed/Btqw56DFhro?si=uZ8JDu488OEmJjMJ) 7 | 8 | 9 | The YAML file in this directory should be modified according to your needs and placed in `/etc/kvmd/override.yaml`. More details can be found in the upstream [docs](https://docs.pikvm.org/first_steps/). -------------------------------------------------------------------------------- /2024-05-pikvm/tesmart-kvmd-override.yaml: -------------------------------------------------------------------------------- 1 | #[kvmd-webterm@pikvm ~]$ cat /etc/kvmd/override.yaml 2 | kvmd: 3 | hid: 4 | mouse: 5 | absolute: false 6 | gpio: 7 | drivers: 8 | tes: 9 | type: tesmart 10 | host: 10.42.0.77 11 | port: 5000 12 | reboot: 13 | type: cmd 14 | cmd: [/usr/bin/sudo, reboot] 15 | restart_service: 16 | type: cmd 17 | cmd: [/usr/bin/sudo, systemctl, restart, kvmd] 18 | scheme: 19 | ch0_led: 20 | driver: tes 21 | pin: 0 22 | mode: input 23 | ch1_led: 24 | driver: tes 25 | pin: 1 26 | mode: input 27 | ch2_led: 28 | driver: tes 29 | pin: 2 30 | mode: input 31 | ch3_led: 32 | driver: tes 33 | pin: 3 34 | mode: input 35 | ch4_led: 36 | driver: tes 37 | pin: 4 38 | mode: input 39 | ch5_led: 40 | driver: tes 41 | pin: 5 42 | mode: input 43 | ch6_led: 44 | driver: tes 45 | pin: 6 46 | mode: input 47 | ch7_led: 48 | driver: tes 49 | pin: 7 50 | mode: input 51 | pikvm_led: 52 | pin: 0 53 | mode: input 54 | ch0_button: 55 | driver: tes 56 | pin: 0 57 | mode: output 58 | switch: false 59 | ch1_button: 60 | driver: tes 61 | pin: 1 62 | mode: output 63 | switch: false 64 | ch2_button: 65 | driver: tes 66 | pin: 2 67 | mode: output 68 | switch: false 69 | ch3_button: 70 | driver: tes 71 | pin: 3 72 | mode: output 73 | switch: false 74 | ch4_button: 75 | driver: tes 76 | pin: 4 77 | mode: output 78 | switch: false 79 | ch5_button: 80 | driver: tes 81 | pin: 5 82 | mode: output 83 | switch: false 84 | ch6_button: 85 | driver: tes 86 | pin: 6 87 | mode: output 88 | switch: false 89 | ch7_button: 90 | driver: tes 91 | pin: 7 92 | mode: output 93 | switch: false 94 | reboot_button: 95 | driver: reboot 96 | pin: 0 97 | mode: output 98 | switch: false 99 | restart_service_button: 100 | driver: restart_service 101 | pin: 0 102 | mode: output 103 | switch: false 104 | view: 105 | table: 106 | - ["#morpheus", ch0_led, ch0_button] 107 | - ["#deep-thought", ch1_led, ch1_button] 108 | - ["#hl15", ch2_led, ch2_button] 109 | - ["#notinuse", ch3_led, ch3_button] 110 | - ["#notinuse", ch4_led, ch4_button] 111 | - ["#opnsense", ch5_led, ch5_button] 112 | - ["#zoidberg", ch6_led, ch6_button] 113 | - ["#homeassistant", ch7_led, ch7_button] 114 | - ["#PiKVM", "pikvm_led|green", "restart_service_button|confirm|Service", "reboot_button|confirm|Reboot"] -------------------------------------------------------------------------------- /2024-06-subnet-routers/README.md: -------------------------------------------------------------------------------- 1 | # Subnet Routers | Tailscale Explained 2 | 3 | This readme accompanies our YouTube video about subnet routers. 4 | 5 | [![Subnet Routers | Tailscale Explained](https://img.youtube.com/vi/UmVMaymH1-s/maxresdefault.jpg)](https://www.youtube.com/watch?v=UmVMaymH1-s) 6 | 7 | ## ACL snippet for autoApprovers 8 | 9 | + https://tailscale.com/kb/1337/acl-syntax#autoapprovers 10 | 11 | Copy and paste the snippet below into your access controls file and modify accordingly to start making use of `autoApprovers`. 12 | 13 | ``` 14 | "autoApprovers": { 15 | "routes": { 16 | "192.168.79.0/24": ["group:admin", "amelie@tailandscales.com", "tag:dev"], 17 | }, 18 | "exitNode": ["tag:dev"], 19 | }, 20 | ``` 21 | -------------------------------------------------------------------------------- /2024-07-cloud-init/01-cloud-init-do-manual/README.md: -------------------------------------------------------------------------------- 1 | # Use cloud-init manually 2 | 3 | Always refer to [this KB doc](https://tailscale.com/kb/1293/cloud-init) first, it is more actively maintained than this repo. 4 | 5 | ## Using an auth key 6 | 7 | Inject this code snippet to use an auth key to automatically add a node to your tailnet. 8 | 9 | If using Digitalocean as per the video accompanying this guide, look under "advanced options" and check the "Add Initialization scripts (free)" option. Paste the snippet below into the text box. For more information on using Digitalocean systems with cloud-init specifically, refer to their [documentation](https://docs.digitalocean.com/products/droplets/how-to/automate-setup-with-cloud-init/). 10 | 11 | ``` 12 | #cloud-config 13 | # The above header must generally appear on the first line of a cloud config 14 | # file, but all other lines that begin with a # are optional comments. 15 | 16 | runcmd: 17 | # One-command install, from https://tailscale.com/download/ 18 | - ['sh', '-c', 'curl -fsSL https://tailscale.com/install.sh | sh'] 19 | # Set sysctl settings for IP forwarding (useful when configuring an exit node) 20 | - ['sh', '-c', "echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf && echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf && sudo sysctl -p /etc/sysctl.d/99-tailscale.conf" ] 21 | # Generate an auth key from your Admin console 22 | # https://login.tailscale.com/admin/settings/keys 23 | # and replace the placeholder below 24 | - ['tailscale', 'up', '--authkey=tskey-auth-123-nguVY4quaJeBrjTwNwZGPeXDvgR8LJ3i'] 25 | # (Optional) Include this line to make this node available over Tailscale SSH 26 | - ['tailscale', 'set', '--ssh'] 27 | # (Optional) Include this line to configure this machine as an exit node 28 | - ['tailscale', 'set', '--advertise-exit-node'] 29 | ``` 30 | 31 | ## Using an OAuth token 32 | 33 | ``` 34 | #cloud-config 35 | # The above header must generally appear on the first line of a cloud config 36 | # file, but all other lines that begin with a # are optional comments. 37 | 38 | runcmd: 39 | # One-command install, from https://tailscale.com/download/ 40 | - ['sh', '-c', 'curl -fsSL https://tailscale.com/install.sh | sh'] 41 | # Set sysctl settings for IP forwarding (useful when configuring an exit node) 42 | - ['sh', '-c', "echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf && echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf && sudo sysctl -p /etc/sysctl.d/99-tailscale.conf" ] 43 | # Generate an auth key from your Admin console 44 | # https://login.tailscale.com/admin/settings/keys 45 | # and replace the placeholder below 46 | - ['tailscale', 'up', '--authkey=tskey-client-exampletoken-VwbYz8VrewLt5UXjT9Lf2M39ydgaAzD92', '--advertise-tags=tag:dev'] 47 | # (Optional) Include this line to make this node available over Tailscale SSH 48 | - ['tailscale', 'set', '--ssh'] 49 | # (Optional) Include this line to configure this machine as an exit node 50 | - ['tailscale', 'set', '--advertise-exit-node'] 51 | ``` -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | nocows = 1 3 | host_key_checking = False 4 | retry_files_enabled = False 5 | roles_path = $PWD/galaxy_roles:$PWD/roles:$PWD/submodules 6 | inventory = ./hosts.ini 7 | vault_password_file = ./.vault-password 8 | 9 | [privilege_escalation] 10 | become_ask_pass = False 11 | 12 | [ssh_connection] 13 | pipelining = True -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/ansible/group_vars/caddy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | caddy_tls_providers: 4 | - provider: cloudflare 5 | challenge_type: dns 6 | provider_api_token: "{{ cloudflare_api_token }}" 7 | resolver_ip: 1.1.1.1 8 | 9 | caddy_endpoints: 10 | - friendly_name: jf 11 | fqdn: jf.initechlabs.dev 12 | upstream: "fakenas:2285" 13 | tls_insecure: false 14 | tls_provider: cloudflare 15 | # - friendly_name: test2 16 | # fqdn: test2.initechlabs.dev 17 | # upstream: "1.2.3.4:8080" 18 | # tls_insecure: true 19 | # tls_provider: cloudflare 20 | -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/ansible/hosts.ini: -------------------------------------------------------------------------------- 1 | [caddy] 2 | nyc1-terraform-caddy ansible_ssh_user=zaphod -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/ansible/requirements.yaml: -------------------------------------------------------------------------------- 1 | roles: 2 | - src: https://github.com/ironicbadger/ansible-role-caddy.git 3 | name: ironicbadger.caddy -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/ansible/run.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: nyc1-terraform-caddy 3 | vars_files: 4 | - 'vars/secrets.yaml' 5 | roles: 6 | - ironicbadger.caddy -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/ansible/vars/secrets.yaml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 35393131386333663839333261323261313538303661383739363261353933376130656266313331 3 | 3630633762356161333937383431383235393436383838300a616230376232663937623731636130 4 | 33326530623834373063646637386263396238343562626639316639393764643536336436653437 5 | 3638313464656134630a333634356636306563613138333535303432326237393466643434366637 6 | 32386164656537633035626362356661323366326634333437616437316665346464346133393131 7 | 33363162393131333661303439663462383431623537623265313562666539636530393366393131 8 | 32633235326263316565326134333732326634326264343832386433643233326165396234636330 9 | 39306634363534393065 -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | jellyfin: 3 | image: jellyfin/jellyfin 4 | container_name: jellyfin 5 | volumes: 6 | - /mnt/appdata/mediaservers/jellyfin:/config 7 | - /mnt/jbod:/mnt/jbod 8 | ports: 9 | - 2285:8096 10 | environment: 11 | - PUID=1000 12 | - PGID=1000 13 | - TZ=America/New_York 14 | - JELLYFIN_PublishedServerUrl=jf.initech.video 15 | hostname: us-rdu 16 | restart: unless-stopped -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S just --justfile 2 | # ^ A shebang isn't required, but allows a justfile to be executed 3 | # like a script, with `./justfile test`, for example. 4 | 5 | ## terraform stuff 6 | init: 7 | cd terraform && terraform init 8 | 9 | apply: 10 | cd terraform && terraform apply 11 | 12 | destroy: 13 | cd terraform && terraform destroy 14 | 15 | ## ansible stuff 16 | run HOST *TAGS: 17 | cd ansible && ansible-playbook -b run.yaml --limit {{HOST}} {{TAGS}} 18 | 19 | ## repo stuff 20 | # optionally use --force to force reinstall all requirements 21 | reqs *FORCE: 22 | cd ansible && ansible-galaxy install -r requirements.yaml {{FORCE}} 23 | 24 | # ansible vault (encrypt/decrypt/edit) 25 | vault ACTION: 26 | cd ansible && EDITOR='code --wait' ansible-vault {{ACTION}} vars/secrets.yaml 27 | -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/terraform/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Get SSH key ID number 4 | 5 | ``` 6 | $ doctl -t dop_v1_d912349ce88 compute ssh-key list 7 | 8 | ID Name FingerPrint 9 | 41133189 laptop d0:3b:39:f0:61:f7:f5:ad:9a:d9:ee:44:92:09:5f:34 10 | 22222222 dev 2a:ad:9a:d9:ca:9c:77:ee:44:92:09:5f:3f:a7:6f:da 11 | ``` -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/terraform/digitalocean.auto.tfvars.example: -------------------------------------------------------------------------------- 1 | do_token = "dop_v1_1234" 2 | tailscale_auth_key = "tskey-auth-1234" -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/terraform/digitalocean.tftpl: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | ssh_pwauth: false 3 | users: 4 | - name: zaphod 5 | gecos: Ansible User 6 | groups: users,admin,wheel 7 | sudo: ALL=(ALL) NOPASSWD:ALL 8 | shell: /bin/bash 9 | lock_passwd: true 10 | 11 | runcmd: 12 | - curl -fsSL https://tailscale.com/install.sh | sh 13 | - ['sh', '-c', "echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf && echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf && sudo sysctl -p /etc/sysctl.d/99-tailscale.conf" ] 14 | - tailscale up --ssh --accept-routes --authkey=${tailscale_auth_key} 15 | #- tailscale set --ssh --advertise-exit-node 16 | - curl -fsSL https://get.docker.com | sh -------------------------------------------------------------------------------- /2024-07-cloud-init/02-cloud-init-do-terraform/terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | digitalocean = { 4 | source = "digitalocean/digitalocean" 5 | version = "~> 2.0" 6 | } 7 | } 8 | } 9 | 10 | variable "do_token" {} 11 | variable "tailscale_auth_key" {} 12 | 13 | provider "digitalocean" { 14 | token = var.do_token 15 | } 16 | 17 | resource "digitalocean_droplet" "droplet" { 18 | image = "ubuntu-24-04-x64" 19 | name = "nyc1-terraform-caddy" 20 | region = "nyc1" 21 | size = "s-1vcpu-1gb" 22 | # doctl -t dop_v1_token compute ssh-key list 23 | ssh_keys = [42950830] 24 | user_data = templatefile("digitalocean.tftpl", { tailscale_auth_key = var.tailscale_auth_key }) 25 | } -------------------------------------------------------------------------------- /2024-11-tsdproxy/compose.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | services: 3 | 4 | ## tsdproxy 5 | tsdproxy: 6 | image: almeidapaulopt/tsdproxy:latest 7 | container_name: tsdproxy 8 | volumes: 9 | - /var/run/docker.sock:/var/run/docker.sock 10 | - tsdproxydata:/data 11 | environment: 12 | # Get AuthKey from your Tailscale account 13 | - TSDPROXY_AUTHKEY=tskey-auth-kC43EMqn1a11CNTRL-notarealkey 14 | # Address of docker server (access to example.com ports) 15 | - TSDPROXY_HOSTNAME=10.42.37.238 16 | - DOCKER_HOST=unix:///var/run/docker.sock 17 | restart: unless-stopped 18 | 19 | ## PDF 20 | stirling-pdf: 21 | image: stirlingtools/stirling-pdf:latest 22 | labels: 23 | tsdproxy.enable: "true" 24 | tsdproxy.name: "pdf" 25 | ports: 26 | - 8080:8080 27 | 28 | ## Audiobooks 29 | abs: 30 | image: advplyr/audiobookshelf 31 | container_name: abs 32 | volumes: 33 | - /home/zaphod/audiobooks:/audiobooks 34 | - audiobooks-metadata:/metadata 35 | - audiobooks-config:/config 36 | labels: 37 | tsdproxy.enable: "true" 38 | tsdproxy.name: "books" 39 | ports: 40 | - 8081:80 41 | restart: unless-stopped 42 | 43 | ## SearXNG 44 | searxng: 45 | container_name: searxng 46 | image: docker.io/searxng/searxng:latest 47 | restart: unless-stopped 48 | labels: 49 | tsdproxy.enable: "true" 50 | tsdproxy.name: "search" 51 | ports: 52 | - 8082:8080 53 | volumes: 54 | - ./searxng:/etc/searxng:rw 55 | environment: 56 | - SEARXNG_BASE_URL=https://search.your.domain.com/ 57 | cap_drop: 58 | - ALL 59 | cap_add: 60 | - CHOWN 61 | - SETGID 62 | - SETUID 63 | logging: 64 | driver: "json-file" 65 | options: 66 | max-size: "1m" 67 | max-file: "1" 68 | redis: 69 | container_name: redis 70 | image: docker.io/valkey/valkey:8-alpine 71 | command: valkey-server --save 30 1 --loglevel warning 72 | restart: unless-stopped 73 | volumes: 74 | - valkey-data2:/data 75 | cap_drop: 76 | - ALL 77 | cap_add: 78 | - SETGID 79 | - SETUID 80 | - DAC_OVERRIDE 81 | logging: 82 | driver: "json-file" 83 | options: 84 | max-size: "1m" 85 | max-file: "1" 86 | 87 | volumes: 88 | valkey-data2: 89 | audiobooks-config: 90 | audiobooks-metadata: 91 | tsdproxydata: -------------------------------------------------------------------------------- /2024-12-tsidp/install-tsidp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on any error 4 | set -e 5 | 6 | # Must run as root 7 | if [ "$EUID" -ne 0 ]; then 8 | echo "Please run as root" 9 | exit 1 10 | fi 11 | 12 | # Create required directories 13 | echo "Creating directories..." 14 | mkdir -p /var/lib/tsidp 15 | 16 | # Set directory permissions 17 | echo "Setting permissions..." 18 | chown root:root /var/lib/tsidp 19 | chmod 700 /var/lib/tsidp 20 | 21 | # Create environment file with secure permissions 22 | echo "Creating environment file..." 23 | touch /etc/default/tsidp 24 | chown root:root /etc/default/tsidp 25 | chmod 600 /etc/default/tsidp 26 | 27 | # Create the environment file content 28 | cat > /etc/default/tsidp << EOF 29 | HOME=. 30 | TS_HOSTNAME=tsidp 31 | TS_AUTHKEY=tskey-auth-k1234 32 | TS_STATE_DIR=/var/lib/tsidp 33 | TS_USERSPACE=false 34 | TAILSCALE_USE_WIP_CODE=1 35 | EOF 36 | 37 | # Create systemd service file 38 | echo "Creating systemd service..." 39 | cat > /etc/systemd/system/tsidp.service << EOF 40 | [Unit] 41 | Description=TSIDP Service 42 | After=network-online.target 43 | Wants=network-online.target 44 | 45 | [Service] 46 | Type=simple 47 | User=root 48 | WorkingDirectory=/usr/local 49 | EnvironmentFile=/etc/default/tsidp 50 | ExecStart=/usr/local/bin/tsidp 51 | Restart=always 52 | RestartSec=5 53 | 54 | [Install] 55 | WantedBy=multi-user.target 56 | EOF 57 | 58 | echo "Setup complete! Don't forget to:" 59 | echo "1. Copy your binary to /usr/local/bin/tsidp" 60 | echo "2. Run: systemctl daemon-reload" 61 | echo "3. Run: systemctl enable --now tsidp.service" 62 | -------------------------------------------------------------------------------- /2024-12-tsidp/tsidp.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tailscale-dev/video-code-snippets/2cd9ab4f8efe836b0ae0e73ab70c531cf5078e17/2024-12-tsidp/tsidp.zip -------------------------------------------------------------------------------- /2025-01-beszel/beszel-hub/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ts-beszel-hub: 3 | image: tailscale/tailscale:latest 4 | container_name: ts-beszel-hub 5 | hostname: beszel 6 | environment: 7 | - TS_AUTHKEY=tskey-client-not-a-real-token-Hsi1 8 | - TS_EXTRA_ARGS=--advertise-tags=tag:beszel 9 | - TS_SERVE_CONFIG=/config/beszel-hub.json 10 | - TS_STATE_DIR=/var/lib/tailscale 11 | - TS_USERSPACE=true 12 | volumes: 13 | - ${PWD}/beszel-hub/tailscale/state:/var/lib/tailscale 14 | - ${PWD}/beszel-hub/tailscale/config:/config 15 | restart: unless-stopped 16 | beszel: 17 | image: henrygd/beszel:latest 18 | container_name: beszel-hub 19 | network_mode: service:ts-beszel-hub 20 | depends_on: 21 | - ts-beszel-hub 22 | volumes: 23 | - ${PWD}/beszel-hub/beszel_data:/beszel_data 24 | restart: unless-stopped -------------------------------------------------------------------------------- /2025-01-beszel/beszel-hub/config/beszel-hub.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCP": { 3 | "443": { 4 | "HTTPS": true 5 | } 6 | }, 7 | "Web": { 8 | "${TS_CERT_DOMAIN}:443": { 9 | "Handlers": { 10 | "/": { 11 | "Proxy": "http://127.0.0.1:8090" 12 | } 13 | } 14 | } 15 | }, 16 | "AllowFunnel": { 17 | "${TS_CERT_DOMAIN}:443": false 18 | } 19 | } -------------------------------------------------------------------------------- /2025-02-metrics/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | grafana-ts: 4 | image: tailscale/tailscale:latest 5 | container_name: grafana-ts 6 | hostname: grafana 7 | volumes: 8 | - ${PWD}/grafana/tailscale/state:/var/lib/tailscale 9 | - /dev/net/tun:/dev/net/tun 10 | cap_add: 11 | - NET_ADMIN 12 | - NET_RAW 13 | environment: 14 | - "TS_AUTH_KEY=tskey-auth-1234" 15 | - TS_STATE_DIR=/var/lib/tailscale 16 | - TS_ENABLE_METRICS=true 17 | restart: unless-stopped 18 | 19 | grafana: 20 | image: grafana/grafana:latest 21 | container_name: grafana 22 | restart: unless-stopped 23 | network_mode: service:grafana-ts 24 | environment: 25 | - GF_SECURITY_ADMIN_USER=admin 26 | - GF_SECURITY_ADMIN_PASSWORD=admin 27 | volumes: 28 | - ${PWD}/grafana:/var/lib/grafana 29 | 30 | 31 | prometheus-ts: 32 | image: tailscale/tailscale:latest 33 | container_name: prometheus-ts 34 | hostname: prometheus 35 | volumes: 36 | - ${PWD}/prometheus/tailscale/state:/var/lib/tailscale 37 | - /dev/net/tun:/dev/net/tun 38 | cap_add: 39 | - NET_ADMIN 40 | - NET_RAW 41 | environment: 42 | - "TS_AUTH_KEY=tskey-auth-1234" 43 | - TS_STATE_DIR=/var/lib/tailscale 44 | - TS_ENABLE_METRICS=true 45 | restart: unless-stopped 46 | 47 | prometheus: 48 | image: prom/prometheus:latest 49 | container_name: prometheus 50 | network_mode: service:prometheus-ts 51 | restart: unless-stopped 52 | command: 53 | - '--config.file=/etc/prometheus/prometheus.yml' 54 | - '--storage.tsdb.path=/prometheus' 55 | - '--storage.tsdb.retention.time=30d' 56 | - '--web.enable-lifecycle' 57 | volumes: 58 | - ${PWD}/prometheus/etc:/etc/prometheus 59 | - ${PWD}/prometheus/data:/prometheus 60 | 61 | node-exporter: 62 | image: prom/node-exporter:latest 63 | container_name: node-exporter 64 | restart: unless-stopped 65 | volumes: 66 | - /proc:/host/proc:ro 67 | - /sys:/host/sys:ro 68 | - /:/rootfs:ro 69 | command: 70 | - '--path.procfs=/host/proc' 71 | - '--path.rootfs=/rootfs' 72 | - '--path.sysfs=/host/sys' 73 | - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)' 74 | ports: 75 | - "9100:9100" 76 | networks: 77 | - monitoring 78 | 79 | cadvisor: 80 | image: gcr.io/cadvisor/cadvisor:latest 81 | container_name: cadvisor 82 | restart: unless-stopped 83 | volumes: 84 | - /:/rootfs:ro 85 | - /var/run:/var/run:ro 86 | - /sys:/sys:ro 87 | - /var/lib/docker/:/var/lib/docker:ro 88 | - /dev/disk/:/dev/disk:ro 89 | ports: 90 | - "8080:8080" 91 | networks: 92 | - monitoring 93 | 94 | networks: 95 | monitoring: 96 | driver: bridge -------------------------------------------------------------------------------- /2025-02-metrics/grafana/grafana.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "type": "dashboard" 15 | } 16 | ] 17 | }, 18 | "editable": true, 19 | "fiscalYearStartMonth": 0, 20 | "graphTooltip": 0, 21 | "id": 186, 22 | "links": [], 23 | "panels": [ 24 | { 25 | "datasource": { 26 | "type": "prometheus", 27 | "uid": "${DS}" 28 | }, 29 | "fieldConfig": { 30 | "defaults": { 31 | "color": { 32 | "mode": "palette-classic" 33 | }, 34 | "mappings": [], 35 | "thresholds": { 36 | "mode": "absolute", 37 | "steps": [ 38 | { 39 | "color": "green", 40 | "value": null 41 | } 42 | ] 43 | }, 44 | "unit": "bytes" 45 | }, 46 | "overrides": [] 47 | }, 48 | "gridPos": { 49 | "h": 8, 50 | "w": 12, 51 | "x": 0, 52 | "y": 0 53 | }, 54 | "id": 1, 55 | "options": { 56 | "minVizHeight": 75, 57 | "minVizWidth": 75, 58 | "orientation": "auto", 59 | "reduceOptions": { 60 | "calcs": ["lastNotNull"], 61 | "fields": "", 62 | "values": false 63 | }, 64 | "showThresholdLabels": false, 65 | "showThresholdMarkers": true, 66 | "sizing": "auto" 67 | }, 68 | "pluginVersion": "11.5.1", 69 | "targets": [ 70 | { 71 | "datasource": { 72 | "type": "prometheus", 73 | "uid": "${DS}" 74 | }, 75 | "editorMode": "code", 76 | "expr": "sum(rate(tailscaled_inbound_bytes_total[5m]))", 77 | "legendFormat": "Inbound Bytes", 78 | "range": true, 79 | "refId": "A" 80 | }, 81 | { 82 | "datasource": { 83 | "type": "prometheus", 84 | "uid": "${DS}" 85 | }, 86 | "editorMode": "code", 87 | "expr": "sum(rate(tailscaled_outbound_bytes_total[5m]))", 88 | "legendFormat": "Outbound Bytes", 89 | "range": true, 90 | "refId": "B" 91 | } 92 | ], 93 | "title": "Network Traffic", 94 | "type": "gauge" 95 | }, 96 | { 97 | "datasource": { 98 | "type": "prometheus", 99 | "uid": "${DS}" 100 | }, 101 | "fieldConfig": { 102 | "defaults": { 103 | "color": { 104 | "mode": "palette-classic" 105 | }, 106 | "custom": { 107 | "axisBorderShow": false, 108 | "axisCenteredZero": false, 109 | "axisColorMode": "text", 110 | "axisLabel": "", 111 | "axisPlacement": "auto", 112 | "barAlignment": 0, 113 | "barWidthFactor": 0.6, 114 | "drawStyle": "line", 115 | "fillOpacity": 0, 116 | "gradientMode": "none", 117 | "hideFrom": { 118 | "legend": false, 119 | "tooltip": false, 120 | "viz": false 121 | }, 122 | "insertNulls": false, 123 | "lineInterpolation": "smooth", 124 | "lineWidth": 2, 125 | "pointSize": 5, 126 | "scaleDistribution": { 127 | "type": "linear" 128 | }, 129 | "showPoints": "never", 130 | "spanNulls": true, 131 | "stacking": { 132 | "group": "A", 133 | "mode": "none" 134 | }, 135 | "thresholdsStyle": { 136 | "mode": "off" 137 | } 138 | }, 139 | "mappings": [], 140 | "thresholds": { 141 | "mode": "absolute", 142 | "steps": [ 143 | { 144 | "color": "green", 145 | "value": null 146 | } 147 | ] 148 | }, 149 | "unit": "pps" 150 | }, 151 | "overrides": [] 152 | }, 153 | "gridPos": { 154 | "h": 8, 155 | "w": 12, 156 | "x": 12, 157 | "y": 0 158 | }, 159 | "id": 2, 160 | "options": { 161 | "legend": { 162 | "calcs": [], 163 | "displayMode": "list", 164 | "placement": "bottom", 165 | "showLegend": true 166 | }, 167 | "tooltip": { 168 | "hideZeros": false, 169 | "mode": "multi", 170 | "sort": "none" 171 | } 172 | }, 173 | "pluginVersion": "11.5.1", 174 | "targets": [ 175 | { 176 | "datasource": { 177 | "type": "prometheus", 178 | "uid": "${DS}" 179 | }, 180 | "editorMode": "code", 181 | "expr": "rate(tailscaled_inbound_packets_total{path=~\"$path\"}[5m])", 182 | "legendFormat": "inbound - {{path}}", 183 | "range": true, 184 | "refId": "A" 185 | }, 186 | { 187 | "datasource": { 188 | "type": "prometheus", 189 | "uid": "${DS}" 190 | }, 191 | "editorMode": "code", 192 | "expr": "rate(tailscaled_outbound_packets_total{path=~\"$path\"}[5m])", 193 | "hide": false, 194 | "legendFormat": "outbound - {{path}}", 195 | "range": true, 196 | "refId": "B" 197 | } 198 | ], 199 | "title": "Packets by Path", 200 | "type": "timeseries" 201 | }, 202 | { 203 | "datasource": { 204 | "type": "prometheus", 205 | "uid": "${DS}" 206 | }, 207 | "fieldConfig": { 208 | "defaults": { 209 | "color": { 210 | "mode": "thresholds" 211 | }, 212 | "mappings": [], 213 | "thresholds": { 214 | "mode": "absolute", 215 | "steps": [ 216 | { 217 | "color": "green", 218 | "value": null 219 | }, 220 | { 221 | "color": "red", 222 | "value": 1 223 | } 224 | ] 225 | } 226 | }, 227 | "overrides": [] 228 | }, 229 | "gridPos": { 230 | "h": 8, 231 | "w": 12, 232 | "x": 0, 233 | "y": 8 234 | }, 235 | "id": 3, 236 | "options": { 237 | "colorMode": "value", 238 | "graphMode": "area", 239 | "justifyMode": "auto", 240 | "orientation": "auto", 241 | "percentChangeColorMode": "standard", 242 | "reduceOptions": { 243 | "calcs": ["lastNotNull"], 244 | "fields": "", 245 | "values": false 246 | }, 247 | "showPercentChange": false, 248 | "text": {}, 249 | "textMode": "auto", 250 | "wideLayout": true 251 | }, 252 | "pluginVersion": "11.5.1", 253 | "targets": [ 254 | { 255 | "datasource": { 256 | "type": "prometheus", 257 | "uid": "${DS}" 258 | }, 259 | "editorMode": "code", 260 | "expr": "tailscaled_health_messages{type=\"warning\"}", 261 | "range": true, 262 | "refId": "A" 263 | } 264 | ], 265 | "title": "Health Warnings", 266 | "type": "stat" 267 | }, 268 | { 269 | "datasource": { 270 | "type": "prometheus", 271 | "uid": "${DS}" 272 | }, 273 | "fieldConfig": { 274 | "defaults": { 275 | "color": { 276 | "mode": "thresholds" 277 | }, 278 | "mappings": [], 279 | "thresholds": { 280 | "mode": "absolute", 281 | "steps": [ 282 | { 283 | "color": "green", 284 | "value": null 285 | } 286 | ] 287 | } 288 | }, 289 | "overrides": [] 290 | }, 291 | "gridPos": { 292 | "h": 8, 293 | "w": 12, 294 | "x": 12, 295 | "y": 8 296 | }, 297 | "id": 4, 298 | "options": { 299 | "colorMode": "value", 300 | "graphMode": "area", 301 | "justifyMode": "auto", 302 | "orientation": "auto", 303 | "percentChangeColorMode": "standard", 304 | "reduceOptions": { 305 | "calcs": ["lastNotNull"], 306 | "fields": "", 307 | "values": false 308 | }, 309 | "showPercentChange": false, 310 | "textMode": "auto", 311 | "wideLayout": true 312 | }, 313 | "pluginVersion": "11.5.1", 314 | "targets": [ 315 | { 316 | "datasource": { 317 | "type": "prometheus", 318 | "uid": "${DS}" 319 | }, 320 | "editorMode": "code", 321 | "expr": "tailscaled_advertised_routes", 322 | "legendFormat": "Advertised Routes", 323 | "range": true, 324 | "refId": "A" 325 | }, 326 | { 327 | "datasource": { 328 | "type": "prometheus", 329 | "uid": "${DS}" 330 | }, 331 | "editorMode": "code", 332 | "expr": "tailscaled_approved_routes", 333 | "legendFormat": "Approved Routes", 334 | "range": true, 335 | "refId": "B" 336 | } 337 | ], 338 | "title": "Routes", 339 | "type": "stat" 340 | } 341 | ], 342 | "preload": false, 343 | "refresh": "5s", 344 | "schemaVersion": 40, 345 | "tags": ["tailscale", "networking"], 346 | "templating": { 347 | "list": [ 348 | { 349 | "allowCustomValue": false, 350 | "label": "Datasource", 351 | "name": "DS", 352 | "options": [], 353 | "query": "prometheus", 354 | "refresh": 1, 355 | "regex": "", 356 | "type": "datasource" 357 | }, 358 | { 359 | "allowCustomValue": false, 360 | "current": { 361 | "text": ["All"], 362 | "value": ["$__all"] 363 | }, 364 | "datasource": { 365 | "type": "prometheus", 366 | "uid": "${DS}" 367 | }, 368 | "definition": "label_values(tailscaled_inbound_packets_total,path)", 369 | "description": "", 370 | "includeAll": true, 371 | "multi": true, 372 | "name": "path", 373 | "options": [], 374 | "query": { 375 | "qryType": 1, 376 | "query": "label_values(tailscaled_inbound_packets_total,path)", 377 | "refId": "PrometheusVariableQueryEditor-VariableQuery" 378 | }, 379 | "refresh": 1, 380 | "regex": "", 381 | "type": "query" 382 | } 383 | ] 384 | }, 385 | "time": { 386 | "from": "now-24h", 387 | "to": "now" 388 | }, 389 | "timepicker": {}, 390 | "timezone": "", 391 | "title": "Tailscale Metrics", 392 | "uid": "dec6gflju5wxsa", 393 | "version": 2, 394 | "weekStart": "" 395 | } 396 | -------------------------------------------------------------------------------- /2025-02-metrics/loadgen/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3-slim 2 | 3 | WORKDIR /app 4 | COPY highload.py . 5 | RUN pip install requests 6 | 7 | CMD ["python", "highload.py"] -------------------------------------------------------------------------------- /2025-02-metrics/loadgen/README.md: -------------------------------------------------------------------------------- 1 | Use the Python script to generate load for testing purposes. 2 | 3 | `docker run -v $PWD/highload.py:/app/highload.py --rm -it $(docker build -q .)` -------------------------------------------------------------------------------- /2025-02-metrics/loadgen/highload.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import time 3 | import random 4 | from concurrent.futures import ThreadPoolExecutor 5 | from itertools import cycle 6 | import os 7 | 8 | PROMETHEUS_URL = "http://prometheus.velociraptor-noodlefish.ts.net:9090" 9 | ENDPOINTS = [ 10 | "/api/v1/query?query=up", 11 | "/api/v1/query?query=rate(tailscaled_inbound_bytes_total[5m])", 12 | "/api/v1/query_range?query=tailscaled_health_messages&start=2024-02-05T00:00:00Z&end=2024-02-05T01:00:00Z&step=15s", 13 | "/metrics", 14 | "/api/v1/labels", 15 | "/api/v1/targets" 16 | ] 17 | 18 | NUM_WORKERS = 10 # Concurrent threads 19 | REQUESTS_PER_BATCH = 20 # Requests per worker 20 | 21 | def make_request(endpoint): 22 | try: 23 | response = requests.get(f"{PROMETHEUS_URL}{endpoint}", timeout=2) 24 | status = response.status_code 25 | print(f"Request to {endpoint}: {status}", flush=True) 26 | return status 27 | except Exception as e: 28 | print(f"Error requesting {endpoint}: {e}", flush=True) 29 | return 0 30 | 31 | def worker(worker_id): 32 | endpoints_cycle = cycle(ENDPOINTS) 33 | requests_made = 0 34 | 35 | while True: 36 | endpoint = next(endpoints_cycle) 37 | make_request(endpoint) 38 | requests_made += 1 39 | 40 | if requests_made % 100 == 0: 41 | print(f"Worker {worker_id} has made {requests_made} requests", flush=True) 42 | 43 | time.sleep(random.uniform(0.01, 0.1)) # Small random delay 44 | 45 | def generate_load(): 46 | print(f"Starting load generation with {NUM_WORKERS} workers...") 47 | with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor: 48 | futures = [executor.submit(worker, i) for i in range(NUM_WORKERS)] 49 | try: 50 | # Wait for all futures to complete (they won't unless interrupted) 51 | for future in futures: 52 | future.result() 53 | except KeyboardInterrupt: 54 | print("Stopping load generation...") 55 | 56 | if __name__ == "__main__": 57 | print("Initializing high-load generator...") 58 | generate_load() -------------------------------------------------------------------------------- /2025-02-metrics/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | evaluation_interval: 15s 4 | 5 | rule_files: 6 | - "alert_rules.yml" 7 | 8 | alerting: 9 | alertmanagers: 10 | - static_configs: 11 | - targets: 12 | - alertmanager:9093 13 | 14 | scrape_configs: 15 | - job_name: 'prometheus' 16 | static_configs: 17 | - targets: ['localhost:9090'] 18 | 19 | - job_name: 'node' 20 | static_configs: 21 | - targets: ['node-exporter:9100'] 22 | 23 | - job_name: 'cadvisor' 24 | static_configs: 25 | - targets: ['cadvisor:8080'] 26 | 27 | # - job_name: 'promcontainer' 28 | # static_configs: 29 | # - targets: ['prometheus:9002'] -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | retry_files_enabled = False 4 | roles_path = $PWD/roles 5 | inventory = ./hosts.ini 6 | 7 | [privilege_escalation] 8 | become_ask_pass = False 9 | 10 | [git] 11 | recursive = true # git submodules -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/ansible/hosts.ini: -------------------------------------------------------------------------------- 1 | [nix-llm] 2 | nix-llm ansible_ssh_user=root -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/ansible/playbook.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: nix-llm 3 | roles: 4 | - role: ironicbadger.docker-compose-generator 5 | tags: compose -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/ansible/services/nix-llm/01-ai.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # AI 3 | ollama: 4 | image: ollama/ollama 5 | container_name: ollama 6 | volumes: 7 | - "/opt/appdata/apps/ollama:/root/.ollama" 8 | ports: 9 | - 11434:11434 10 | deploy: 11 | resources: 12 | reservations: 13 | devices: 14 | - driver: cdi 15 | capabilities: 16 | - gpu 17 | device_ids: 18 | - nvidia.com/gpu=all 19 | restart: unless-stopped 20 | openwebui: 21 | image: ghcr.io/open-webui/open-webui:main 22 | container_name: openwebui 23 | ports: 24 | - 8080:8080 25 | # network_mode: service:ts-openwebui 26 | volumes: 27 | - "/opt/appdata/openwebui/data:/app/backend/data" 28 | environment: 29 | - OLLAMA_BASE_URL=http://ollama:11434 30 | restart: unless-stopped 31 | # ts-openwebui: 32 | # image: ghcr.io/tailscale/tailscale:latest 33 | # container_name: ts-beszel-hub 34 | # hostname: beszel 35 | # environment: 36 | # - TS_AUTHKEY=tskey-client-not-a-real-token-Hsi1 37 | # - TS_EXTRA_ARGS=--advertise-tags=tag:web 38 | # - TS_SERVE_CONFIG=/config/openwebui.json 39 | # - TS_STATE_DIR=/var/lib/tailscale 40 | # - TS_USERSPACE=true 41 | # volumes: 42 | # - /opt/appdata/openwebui/tailscale/state:/var/lib/tailscale 43 | # - /opt/appdata/openwebui/tailscale/config:/config 44 | # restart: unless-stopped -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/ansible/services/nix-llm/openwebui.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCP": { 3 | "443": { 4 | "HTTPS": true 5 | } 6 | }, 7 | "Web": { 8 | "${TS_CERT_DOMAIN}:443": { 9 | "Handlers": { 10 | "/": { 11 | "Proxy": "http://127.0.0.1:8080" 12 | } 13 | } 14 | } 15 | }, 16 | "AllowFunnel": { 17 | "${TS_CERT_DOMAIN}:443": false 18 | } 19 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S just --justfile 2 | 3 | ## repo configuration 4 | sub-update: 5 | cd .. && git submodule update --init --recursive 6 | 7 | ## nix installation 8 | install IP: 9 | ssh -o "StrictHostKeyChecking no" nixos@{{IP}} "sudo bash -c '\ 10 | nix-shell -p git --run \"cd /root/ && \ 11 | git clone -b nix-nvidia https://github.com/tailscale-dev/video-code-snippets.git && \ 12 | cd video-code-snippets/2025-02-nix-nvidia-ollama/nix/hosts/nixos/nix-llm/ && \ 13 | sh install-nix.sh\"'" 14 | 15 | ## nix updates 16 | hostname := `hostname | cut -d "." -f 1` 17 | [linux] 18 | switch target_host=hostname: 19 | cd nix && sudo nixos-rebuild switch --flake .#{{target_host}} 20 | 21 | ## copy docker compose yaml to remote host via Ansible 22 | compose HOST *V: 23 | cd ansible && ansible-playbook playbook.yaml --limit {{HOST}} --tags compose {{V}} 24 | -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1740339700, 6 | "narHash": "sha256-cbrw7EgQhcdFnu6iS3vane53bEagZQy/xyIkDWpCgVE=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "04ef94c4c1582fd485bbfdb8c4a8ba250e359195", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-24.11", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "nixpkgs-unstable": { 20 | "locked": { 21 | "lastModified": 1740367490, 22 | "narHash": "sha256-WGaHVAjcrv+Cun7zPlI41SerRtfknGQap281+AakSAw=", 23 | "owner": "nixos", 24 | "repo": "nixpkgs", 25 | "rev": "0196c0175e9191c474c26ab5548db27ef5d34b05", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "nixos", 30 | "ref": "nixos-unstable", 31 | "repo": "nixpkgs", 32 | "type": "github" 33 | } 34 | }, 35 | "root": { 36 | "inputs": { 37 | "nixpkgs": "nixpkgs", 38 | "nixpkgs-unstable": "nixpkgs-unstable" 39 | } 40 | } 41 | }, 42 | "root": "root", 43 | "version": 7 44 | } 45 | -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; 4 | nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; 5 | }; 6 | 7 | outputs = { ... }@inputs: 8 | with inputs; 9 | let 10 | inherit (self) outputs; 11 | stateVersion = "24.11"; 12 | libx = import ./lib { inherit inputs outputs stateVersion pkgs; }; 13 | in { 14 | nixosConfigurations = { 15 | nix-llm = libx.mkNixos { 16 | system = "x86_64-linux"; 17 | hostname = "nix-llm"; 18 | username = "zaphod"; 19 | }; 20 | }; 21 | }; 22 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/hosts/common/common-packages.nix: -------------------------------------------------------------------------------- 1 | { inputs, pkgs, unstablePkgs, ... }: 2 | let 3 | inherit (inputs) nixpkgs nixpkgs-unstable; 4 | in 5 | { 6 | environment.systemPackages = with pkgs; [ 7 | ## stable 8 | ansible 9 | drill 10 | figurine 11 | git 12 | htop 13 | iperf3 14 | just 15 | python3 16 | tree 17 | watch 18 | wget 19 | vim 20 | 21 | # requires nixpkgs.config.allowUnfree = true; 22 | vscode-extensions.ms-vscode-remote.remote-ssh 23 | 24 | #nixpkgs-unstable.legacyPackages.${pkgs.system}.beszel 25 | ]; 26 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/hosts/common/nixos-common.nix: -------------------------------------------------------------------------------- 1 | { pkgs, unstablePkgs, lib, inputs, ... }: 2 | let 3 | inherit (inputs) nixpkgs nixpkgs-unstable; 4 | in 5 | { 6 | time.timeZone = "America/New_York"; 7 | system.stateVersion = "24.11"; 8 | 9 | virtualisation = { 10 | docker = { 11 | enable = true; 12 | autoPrune = { 13 | enable = true; 14 | dates = "weekly"; 15 | }; 16 | }; 17 | }; 18 | 19 | nix = { 20 | settings = { 21 | experimental-features = [ "nix-command" "flakes" ]; 22 | warn-dirty = false; 23 | # 500mb buffer 24 | download-buffer-size = 500000000; 25 | }; 26 | # Automate garbage collection 27 | gc = { 28 | automatic = true; 29 | dates = "weekly"; 30 | options = "--delete-older-than 5"; 31 | }; 32 | }; 33 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/hosts/nixos/nix-llm/default.nix: -------------------------------------------------------------------------------- 1 | { config, inputs, pkgs, name, lib, ... }: 2 | 3 | { 4 | imports = 5 | [ 6 | ./hardware-configuration.nix 7 | ./../../common/nixos-common.nix 8 | ./../../common/common-packages.nix 9 | ]; 10 | 11 | # Boot configuration 12 | boot.loader = { 13 | systemd-boot.enable = true; 14 | efi.canTouchEfiVariables = true; 15 | }; 16 | 17 | # Network configuration 18 | networking = { 19 | firewall.enable = false; 20 | hostName = "nix-llm"; 21 | interfaces.ens18 = { 22 | useDHCP = false; 23 | ipv4.addresses = [{ 24 | address = "10.42.37.100"; 25 | prefixLength = 24; 26 | }]; 27 | }; 28 | defaultGateway = "10.42.37.254"; 29 | nameservers = [ "10.42.37.254" ]; 30 | }; 31 | 32 | # System localization 33 | time.timeZone = "America/New_York"; 34 | i18n.defaultLocale = "en_US.UTF-8"; 35 | 36 | services.xserver = { 37 | enable = false; 38 | videoDrivers = [ "nvidia" ]; 39 | }; 40 | 41 | services.openssh = { 42 | enable = true; 43 | settings.PasswordAuthentication = true; 44 | settings.PermitRootLogin = "yes"; 45 | }; 46 | services.qemuGuest.enable = true; 47 | services.tailscale.enable = true; 48 | # services.ollama = { 49 | # enable = true; 50 | # host = "0.0.0.0"; 51 | # }; 52 | 53 | # userland 54 | #home-manager.useGlobalPkgs = true; 55 | #home-manager.useUserPackages = true; 56 | #home-manager.users.zaphod = { imports = [ ./../../../home/zaphod.nix ]; }; 57 | users.users.zaphod = { 58 | isNormalUser = true; 59 | description = "zaphod"; 60 | extraGroups = [ "networkmanager" "wheel" "docker" ]; 61 | packages = with pkgs; [ 62 | #home-manager 63 | ]; 64 | }; 65 | 66 | # Hardware configuration 67 | hardware = { 68 | graphics = { 69 | enable = true; 70 | enable32Bit = true; 71 | }; 72 | nvidia = { 73 | modesetting.enable = true; 74 | open = false; 75 | nvidiaSettings = true; 76 | powerManagement.enable = true; 77 | }; 78 | nvidia-container-toolkit.enable = true; 79 | }; 80 | 81 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/hosts/nixos/nix-llm/disko.nix: -------------------------------------------------------------------------------- 1 | { 2 | disko.devices = { 3 | disk = { 4 | main = { 5 | type = "disk"; 6 | device = "/dev/vda"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | ESP = { 11 | size = "512M"; 12 | type = "EF00"; 13 | name = "ESP"; 14 | content = { 15 | type = "filesystem"; 16 | format = "vfat"; 17 | mountpoint = "/boot"; 18 | mountOptions = [ "umask=0077" ]; 19 | }; 20 | }; 21 | root = { 22 | size = "100%"; 23 | name = "root"; 24 | content = { 25 | type = "filesystem"; 26 | format = "ext4"; 27 | mountpoint = "/"; 28 | }; 29 | }; 30 | }; 31 | }; 32 | }; 33 | }; 34 | }; 35 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/hosts/nixos/nix-llm/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { config, lib, pkgs, modulesPath, ... }: 5 | 6 | { 7 | imports = 8 | [ (modulesPath + "/profiles/qemu-guest.nix") 9 | ]; 10 | 11 | boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "sr_mod" "virtio_blk" ]; 12 | boot.initrd.kernelModules = [ ]; 13 | boot.kernelModules = [ ]; 14 | boot.extraModulePackages = [ ]; 15 | 16 | fileSystems."/" = 17 | { 18 | device = "/dev/vda2"; 19 | fsType = "ext4"; 20 | }; 21 | 22 | fileSystems."/boot" = 23 | { 24 | device = "/dev/vda1"; 25 | fsType = "vfat"; 26 | options = [ "fmask=0022" "dmask=0022" ]; 27 | }; 28 | swapDevices = [ ]; 29 | 30 | # Enabless DHCP on each ethernet and wireless interface. In case of scripted networking 31 | # (the default) this is the recommended approach. When using systemd-networkd it's 32 | # still possible to use this option, but it's recommended to use it in conjunction 33 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 34 | networking.useDHCP = lib.mkDefault true; 35 | # networking.interfaces.ens18.useDHCP = lib.mkDefault true; 36 | 37 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 38 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/hosts/nixos/nix-llm/install-nix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # git clone -b nix-nvidia https://github.com/tailscale-dev/video-code-snippets.git 3 | # cd video-code-snippets/2025-02-nix-nvidia-ollama/nix/hosts/nixos/nix-llm/ 4 | # sh install-nix.sh 5 | 6 | # Check if the script is running as root 7 | if [ "$EUID" -ne 0 ]; then 8 | echo "This script must be run as root" 9 | exit 1 10 | fi 11 | 12 | # disk partitioning etc 13 | nix --experimental-features "nix-command flakes" run github:nix-community/disko -- --mode disko disko.nix 14 | nixos-generate-config --no-filesystems --root /mnt 15 | 16 | # installation 17 | export NIXPKGS_ALLOW_UNFREE=1 18 | cp hardware-configuration.nix /mnt/etc/nixos/ 19 | nixos-install --root /mnt --flake .#nix-llm --impure -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/lib/default.nix: -------------------------------------------------------------------------------- 1 | { inputs, outputs, stateVersion, ... }: 2 | let 3 | helpers = import ./helpers.nix { inherit inputs outputs stateVersion; }; 4 | in 5 | { 6 | inherit (helpers) 7 | mkNixos; 8 | #mkOther; 9 | } -------------------------------------------------------------------------------- /2025-02-nix-nvidia-ollama/nix/lib/helpers.nix: -------------------------------------------------------------------------------- 1 | # lib/helper.nix 2 | { inputs, outputs, stateVersion, ... }: 3 | { 4 | mkNixos = { system, hostname, username, extraModules ? [] }: 5 | let 6 | pkgs = import inputs.nixpkgs { inherit system; config.allowUnfree = true; }; 7 | unstablePkgs = import inputs.nixpkgs-unstable { inherit system; config.allowUnfree = true; }; 8 | in 9 | inputs.nixpkgs.lib.nixosSystem { 10 | inherit system; 11 | specialArgs = { inherit pkgs unstablePkgs inputs system hostname username; }; 12 | modules = [ 13 | (inputs.vscode-server.nixosModules.default or {}) 14 | ../hosts/nixos/${hostname} 15 | ../hosts/common/nixos-common.nix 16 | ] ++ extraModules; 17 | }; 18 | } -------------------------------------------------------------------------------- /2025-04-mazanoke/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | mazanoke: 3 | container_name: mazanoke 4 | image: ghcr.io/civilblur/mazanoke:latest 5 | network_mode: service:ts-mazanoke 6 | depends_on: 7 | - ts-mazanoke 8 | restart: unless-stopped 9 | ts-mazanoke: 10 | image: tailscale/tailscale:latest 11 | hostname: images 12 | environment: 13 | - TS_AUTHKEY=tskey-auth-abc123 14 | - TS_STATE_DIR=/var/lib/tailscale 15 | - TS_SERVE_CONFIG=/config/mazanoke.json 16 | - TS_USERSPACE=true 17 | volumes: 18 | - ${PWD}/mazanoke/config:/config 19 | - ${PWD}/mazanoke/ts-state:/var/lib/tailscale 20 | restart: unless-stopped -------------------------------------------------------------------------------- /2025-04-mazanoke/mazanoke.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCP": { 3 | "443": { 4 | "HTTPS": true 5 | } 6 | }, 7 | "Web": { 8 | "${TS_CERT_DOMAIN}:443": { 9 | "Handlers": { 10 | "/": { 11 | "Proxy": "http://127.0.0.1:80" 12 | } 13 | } 14 | } 15 | }, 16 | "AllowFunnel": { 17 | "${TS_CERT_DOMAIN}:443": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /2025-04-pocketid/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | tr: 3 | image: traefik 4 | container_name: tr 5 | volumes: 6 | - "/opt/appdata/traefik/letsencrypt:/letsencrypt" 7 | - /var/run/docker.sock:/var/run/docker.sock:ro 8 | labels: 9 | - traefik.enable=false 10 | ports: 11 | - 80:80 12 | - 443:443 13 | environment: 14 | - CF_DNS_API_TOKEN=abc123 15 | command: 16 | - --log.level=info 17 | - --accesslog=false 18 | - --api.insecure=true 19 | - --providers.docker=true 20 | - --providers.docker.exposedbydefault=false 21 | - --entrypoints.web.address=:80 22 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 23 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 24 | - --entrypoints.websecure.address=:443 25 | - --entrypoints.websecure.http.tls.certresolver=cloudflare 26 | - --certificatesresolvers.cloudflare.acme.dnschallenge=true 27 | - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare 28 | - --certificatesresolvers.cloudflare.acme.email=admin@initechlabs.dev 29 | - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json 30 | - --serversTransport.insecureSkipVerify=true 31 | restart: unless-stopped 32 | pocketid: 33 | image: ghcr.io/pocket-id/pocket-id 34 | container_name: pocketid 35 | environment: 36 | - PUBLIC_APP_URL=https://id.initechlabs.dev 37 | - TRUST_PROXY=true 38 | labels: 39 | - traefik.enable=true 40 | - "traefik.http.routers.pocketid.rule=Host(`id.initechlabs.dev`)" 41 | volumes: 42 | - "/opt/appdata/pocket-id/data:/app/backend/data" 43 | # Optional healthcheck 44 | healthcheck: 45 | test: "curl -f http://localhost/health" 46 | interval: 1m30s 47 | timeout: 5s 48 | retries: 2 49 | start_period: 10s 50 | restart: unless-stopped 51 | webfinger: 52 | image: nginx 53 | container_name: webfinger 54 | volumes: 55 | - "/opt/appdata/webfinger:/usr/share/nginx/html:ro" 56 | labels: 57 | - traefik.enable=true 58 | - "traefik.http.routers.webfinger.rule=Host(`initechlabs.dev`)" 59 | restart: unless-stopped -------------------------------------------------------------------------------- /2025-05-karakeep/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | karakeep-ts: 3 | image: tailscale/tailscale:latest 4 | hostname: karakeep 5 | environment: 6 | - TS_AUTHKEY=123 7 | - TS_STATE_DIR=/var/lib/tailscale 8 | - TS_USERSPACE=true 9 | volumes: 10 | - ${PWD}/karakeep/config:/config 11 | - ${PWD}/karakeep/ts-state:/var/lib/tailscale 12 | restart: unless-stopped 13 | karakeep-app: 14 | image: ghcr.io/karakeep-app/karakeep:release 15 | container_name: karakeep-app 16 | restart: unless-stopped 17 | volumes: 18 | - ${PWD}/karakeep/data:/data 19 | network_mode: service:karakeep-ts 20 | environment: 21 | - MEILI_ADDR=http://karakeep-meilisearch:7700 22 | - BROWSER_WEB_URL=http://karakeep-chrome:9222 23 | - NEXTAUTH_SECRET=123 24 | - MEILI_MASTER_KEY=123 25 | - NEXTAUTH_URL=https://karakeep.velociraptor-noodlefish.ts.net 26 | - DATA_DIR=/data 27 | - CRAWLER_STORE_SCREENSHOT=true 28 | - CRAWLER_FULL_PAGE_SCREENSHOT=true 29 | - CRAWLER_ENABLE_ADBLOCKER=true 30 | - OPENAI_API_KEY=123 31 | karakeep-chrome: 32 | image: gcr.io/zenika-hub/alpine-chrome:123 33 | container_name: karakeep-chrome 34 | restart: unless-stopped 35 | command: 36 | - --no-sandbox 37 | - --disable-gpu 38 | - --disable-dev-shm-usage 39 | - --remote-debugging-address=0.0.0.0 40 | - --remote-debugging-port=9222 41 | - --hide-scrollbars 42 | karakeep-meilisearch: 43 | image: getmeili/meilisearch:v1.13.3 44 | container_name: karakeep-meilisearch 45 | restart: unless-stopped 46 | environment: 47 | - MEILI_NO_ANALYTICS=true 48 | - MEILI_MASTER_KEY=123 49 | volumes: 50 | - ${PWD}/karakeep/meilisearch:/meili_data 51 | -------------------------------------------------------------------------------- /2025-06-self-hosting-part2/audiobookshelf/audiobookshelf.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCP": { 3 | "443": { 4 | "HTTPS": true 5 | } 6 | }, 7 | "Web": { 8 | "${TS_CERT_DOMAIN}:443": { 9 | "Handlers": { 10 | "/": { 11 | "Proxy": "http://127.0.0.1:80" 12 | } 13 | } 14 | } 15 | }, 16 | "AllowFunnel": { 17 | "${TS_CERT_DOMAIN}:443": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /2025-06-self-hosting-part2/audiobookshelf/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | audiobookshelf-ts: 3 | image: tailscale/tailscale:latest 4 | hostname: audiobooks 5 | environment: 6 | - TS_AUTHKEY=tskey-auth-revokved 7 | - TS_STATE_DIR=/var/lib/tailscale 8 | - TS_SERVE_CONFIG=/config/audiobookshelf.json 9 | - TS_USERSPACE=true 10 | volumes: 11 | - /mnt/ssd1/appdata/audiobookshelf/ts-config:/config 12 | - /mnt/ssd1/appdata/audiobookshelf/ts-state:/var/lib/tailscale 13 | restart: unless-stopped 14 | audiobookshelf: 15 | image: advplyr/audiobookshelf 16 | container_name: audiobookshelf 17 | network_mode: service:audiobookshelf-ts 18 | volumes: 19 | - /mnt/ssd1/media/audiobooks:/audiobooks:ro 20 | - /mnt/ssd1/appdata/audiobookshelf/metadata:/metadata 21 | - /mnt/ssd1/appdata/audiobookshelf/config:/config 22 | restart: unless-stopped -------------------------------------------------------------------------------- /2025-06-self-hosting-part2/immich/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | immich-ts: 3 | image: tailscale/tailscale:latest 4 | hostname: immich 5 | environment: 6 | - TS_AUTHKEY=tskey-auth-revoked 7 | - TS_STATE_DIR=/var/lib/tailscale 8 | - TS_SERVE_CONFIG=/config/immich.json 9 | - TS_USERSPACE=true 10 | volumes: 11 | - /mnt/ssd1/appdata/immich/ts-config:/config 12 | - /mnt/ssd1/appdata/immich/ts-state:/var/lib/tailscale 13 | restart: unless-stopped 14 | immich-server: 15 | container_name: immich_server 16 | image: ghcr.io/immich-app/immich-server:release 17 | # extends: 18 | # file: hwaccel.transcoding.yml 19 | # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding 20 | volumes: 21 | - /mnt/ssd1/data/photos/upload:/usr/src/app/upload 22 | - /etc/localtime:/etc/localtime:ro 23 | network_mode: service:immich-ts 24 | # ports: 25 | # - '2283:2283' 26 | environment: 27 | DB_HOSTNAME: immich-database 28 | DB_PASSWORD: tailscale123 29 | DB_USERNAME: zaphod 30 | DB_DATABASE_NAME: immich 31 | REDIS_HOSTNAME: immich-redis 32 | depends_on: 33 | - immich-redis 34 | - immich-database 35 | restart: unless-stopped 36 | healthcheck: 37 | disable: false 38 | 39 | immich-machine-learning: 40 | container_name: immich_machine_learning 41 | # For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag. 42 | # Example tag: ${IMMICH_VERSION:-release}-cuda 43 | image: ghcr.io/immich-app/immich-machine-learning:release 44 | # extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration 45 | # file: hwaccel.ml.yml 46 | # service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference 47 | volumes: 48 | - model-cache:/cache 49 | restart: unless-stopped 50 | healthcheck: 51 | disable: false 52 | 53 | immich-redis: 54 | container_name: immich_redis 55 | image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884 56 | healthcheck: 57 | test: redis-cli ping || exit 1 58 | restart: unless-stopped 59 | 60 | immich-database: 61 | container_name: immich_postgres 62 | image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1 63 | environment: 64 | POSTGRES_PASSWORD: tailscale123 65 | POSTGRES_USER: zaphod 66 | POSTGRES_DB: immich 67 | POSTGRES_INITDB_ARGS: '--data-checksums' 68 | # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs 69 | # DB_STORAGE_TYPE: 'HDD' 70 | volumes: 71 | - /mnt/ssd1/appdata/immich/database:/var/lib/postgresql/data 72 | restart: unless-stopped 73 | 74 | volumes: 75 | model-cache: -------------------------------------------------------------------------------- /2025-06-self-hosting-part2/immich/immich.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCP": { 3 | "443": { 4 | "HTTPS": true 5 | } 6 | }, 7 | "Web": { 8 | "${TS_CERT_DOMAIN}:443": { 9 | "Handlers": { 10 | "/": { 11 | "Proxy": "http://127.0.0.1:2283" 12 | } 13 | } 14 | } 15 | }, 16 | "AllowFunnel": { 17 | "${TS_CERT_DOMAIN}:443": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tailscale YouTube supporting code snippets 2 | 3 | Welcome to this repo which provides supporting code snippets for [Tailscale](https://tailscale.com/yt) YouTube videos. --------------------------------------------------------------------------------