This commit is contained in:
Graham McIntire 2025-10-12 10:07:12 -05:00
parent f7f825a251
commit b1999d4831
No known key found for this signature in database
81 changed files with 633 additions and 5108 deletions

View file

@ -33,12 +33,12 @@
- rsync - rsync
- tmux - tmux
- nano - nano
- the_silver_searcher - the-silver-searcher
- net-tools - net-tools
- mlocate - plocate
- fail2ban - fail2ban
- munin-node - munin-node
- nethogs # - nethogs # Not available in AlmaLinux EPEL
- pv - pv
- selinux-policy - selinux-policy
- tar - tar

View file

@ -1,96 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ghost-content
namespace: blog
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ghost
namespace: blog
spec:
replicas: 1
selector:
matchLabels:
app: ghost
template:
metadata:
labels:
app: ghost
spec:
containers:
- name: ghost
image: ghost:6
ports:
- containerPort: 2368
name: http
env:
- name: NODE_ENV
value: production
- name: url
value: https://w5isp.com
- name: database__client
value: mysql
- name: database__connection__host
value: "10.0.19.6"
- name: database__connection__port
value: "3306"
- name: database__connection__database
value: ghost
- name: database__connection__user
value: ghost
- name: database__connection__password
valueFrom:
secretKeyRef:
name: ghost-db-secret
key: password
- name: mail__transport
value: SMTP
- name: mail__from
value: noreply@w5isp.com
- name: mail__options__service
value: Sendgrid
- name: mail__options__auth__user
value: apikey
- name: mail__options__auth__pass
valueFrom:
secretKeyRef:
name: ghost-smtp
key: password
optional: true
volumeMounts:
- name: ghost-content
mountPath: /var/lib/ghost/content
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: ghost-content
persistentVolumeClaim:
claimName: ghost-content
---
apiVersion: v1
kind: Service
metadata:
name: ghost
namespace: blog
spec:
selector:
app: ghost
ports:
- port: 80
targetPort: 2368
protocol: TCP
type: LoadBalancer

View file

@ -1,17 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: ghost-tailscale
namespace: blog
annotations:
tailscale.com/expose: "true"
tailscale.com/hostname: "ghost"
tailscale.com/tags: "tag:k8s"
spec:
selector:
app: ghost
ports:
- port: 80
targetPort: 2368
protocol: TCP
type: ClusterIP

View file

@ -1,116 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wavelog-data
namespace: wavelog
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wavelog-uploads
namespace: wavelog
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
containers:
- name: wavelog
image: ghcr.io/wavelog/wavelog:latest
ports:
- containerPort: 80
name: http
env:
- name: WAVELOG_DB_TYPE
value: mysql
- name: WAVELOG_DB_HOST
value: "10.0.19.6"
- name: WAVELOG_DB_PORT
value: "3306"
- name: WAVELOG_DB_NAME
value: wavelog
- name: WAVELOG_DB_USER
value: wavelog
- name: WAVELOG_DB_PASS
valueFrom:
secretKeyRef:
name: wavelog-db-secret
key: password
- name: WAVELOG_BASE_URL
value: https://log.w5isp.com
volumeMounts:
- name: wavelog-data
mountPath: /var/www/wavelog
- name: wavelog-uploads
mountPath: /var/www/wavelog/uploads
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: wavelog-data
persistentVolumeClaim:
claimName: wavelog-data
- name: wavelog-uploads
persistentVolumeClaim:
claimName: wavelog-uploads
---
apiVersion: v1
kind: Service
metadata:
name: wavelog
namespace: wavelog
spec:
selector:
app: wavelog
ports:
- port: 80
targetPort: 80
protocol: TCP
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: wavelog-tailscale
namespace: wavelog
annotations:
tailscale.com/expose: "true"
tailscale.com/hostname: "wavelog"
tailscale.com/tags: "tag:k8s"
spec:
selector:
app: wavelog
ports:
- port: 80
targetPort: 80
protocol: TCP
type: ClusterIP

View file

@ -1,7 +0,0 @@
[defaults]
inventory = inventory.yml
host_key_checking = False
retry_files_enabled = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

View file

@ -1,72 +0,0 @@
---
- name: Bootstrap AlmaLinux server for Ansible management
hosts: caddy
become: yes
vars:
ansible_user: graham # Connect as graham initially
tasks:
- name: Create ansible user
user:
name: ansible
uid: 10001
shell: /bin/bash
home: /home/ansible
create_home: yes
state: present
- name: Add ansible user to wheel group
user:
name: ansible
groups: wheel
append: yes
- name: Ensure .ssh directory exists for ansible user
file:
path: /home/ansible/.ssh
state: directory
owner: ansible
group: ansible
mode: "0700"
- name: Add ansible SSH key to ansible user
authorized_key:
user: ansible
key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFghGMnDdkfNC6JPEhMxrKhYDmNcXjHXp/y/mf3uLtzb graham@mbp14.local"
state: present
- name: Add SSH keys from GitHub to graham user
authorized_key:
user: graham
key: "https://github.com/gmcintire.keys"
state: present
- name: Configure passwordless sudo for ansible user
copy:
content: "ansible ALL=(ALL) NOPASSWD: ALL"
dest: /etc/sudoers.d/ansible
mode: "0440"
validate: "visudo -cf %s"
- name: Install Python 3 (required for Ansible)
dnf:
name:
- python3
- python3-pip
state: present
- name: Set Python 3 as default python
alternatives:
name: python
link: /usr/bin/python
path: /usr/bin/python3
- name: Verify ansible user can sudo
command: sudo -u ansible sudo -l
changed_when: false
register: sudo_test
- name: Show sudo verification
debug:
msg: "Ansible user sudo access verified"
when: sudo_test.rc == 0

View file

@ -1,207 +0,0 @@
---
- name: Configure Caddy reverse proxy with Tailscale on AlmaLinux 10
hosts: caddy
become: yes
gather_facts: yes
strategy: free
vars:
tailscale_key: "{{ lookup('env', 'TAILSCALE_KEY') }}"
tasks:
- name: Enable passwordless sudo on AlmaLinux
ansible.builtin.lineinfile:
path: /etc/sudoers
state: present
regexp: "^%wheel"
line: "%wheel ALL=(ALL) NOPASSWD: ALL"
validate: "visudo -cf %s"
- name: Clean up repository files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /etc/yum.repos.d/caddy.repo
- /etc/yum.repos.d/tailscale-stable.repo
when: false # Only enable if you have issues
- name: Check if EPEL is already installed
ansible.builtin.stat:
path: /etc/yum.repos.d/epel.repo
register: epel_check
- name: Install EPEL repo
ansible.builtin.dnf:
name: epel-release
state: present
when: not epel_check.stat.exists
- name: Check if Caddy COPR repo exists
ansible.builtin.stat:
path: /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:group_caddy:caddy.repo
register: caddy_repo_check
- name: Add Caddy COPR repository
ansible.builtin.command:
cmd: dnf copr enable -y @caddy/caddy
when: not caddy_repo_check.stat.exists
- name: Install all required packages in one transaction
ansible.builtin.dnf:
state: present
name:
- caddy
- firewalld
- htop
- rsync
- tmux
- tar
- unzip
- python3-libsemanage
- python3-policycoreutils
- iptables-services
update_cache: yes
- name: Enable and start firewalld
service:
name: firewalld
state: started
enabled: yes
- name: Configure firewall for Caddy
firewalld:
port: "{{ item }}"
permanent: yes
state: enabled
immediate: yes
loop:
- "80/tcp"
- "443/tcp"
- "22/tcp"
- name: Check if Tailscale is installed
command: which tailscale
register: tailscale_check
changed_when: false
failed_when: false
- name: Install Tailscale on AlmaLinux
block:
- name: Add Tailscale repository
yum_repository:
name: tailscale-stable
description: Tailscale stable
baseurl: https://pkgs.tailscale.com/stable/centos/9/$basearch
repo_gpgcheck: yes
gpgcheck: yes
enabled: yes
gpgkey: https://pkgs.tailscale.com/stable/centos/9/repo.gpg
- name: Install Tailscale
dnf:
name: tailscale
state: present
- name: Enable and start Tailscale daemon
service:
name: tailscaled
state: started
enabled: yes
when: tailscale_check.rc != 0
- name: Allow Tailscale through firewall
firewalld:
port: 41641/udp
permanent: yes
state: enabled
immediate: yes
- name: Check if Tailscale is already authenticated
command: tailscale status --json
register: tailscale_status_check
changed_when: false
failed_when: false
- name: Authenticate Tailscale
command: tailscale up --authkey="{{ tailscale_key }}" --hostname=caddy
when:
- tailscale_key is defined
- tailscale_key != ""
- tailscale_status_check.rc != 0 or (tailscale_status_check.stdout | from_json).BackendState != "Running"
register: tailscale_auth
failed_when: false
ignore_errors: yes
- name: Get Tailscale status
command: tailscale status
register: tailscale_final_status
changed_when: false
failed_when: false
- name: Show final Tailscale status
debug:
var: tailscale_final_status.stdout_lines
- name: Enable and start Caddy
service:
name: caddy
state: started
enabled: yes
- name: Deploy Caddyfile configuration
template:
src: templates/Caddyfile.j2
dest: /etc/caddy/Caddyfile
owner: caddy
group: caddy
mode: "0644"
backup: yes
notify: reload caddy
- name: SELinux - Allow Caddy to proxy
seboolean:
name: httpd_can_network_connect
state: yes
persistent: yes
- name: SELinux - Allow Caddy to bind to ports
seport:
ports: "80,443"
proto: tcp
setype: http_port_t
state: present
# Note about SSH forwarding:
# Since SSH doesn't support hostname-based routing, you have two options:
# 1. Use git.w5isp.com:22 which forwards ALL SSH to Forgejo (can't SSH to caddy directly)
# 2. Use different ports: caddy.w5isp.com:22 for server, git.w5isp.com:2222 for git
# 3. Configure client-side SSH config to use ProxyJump through caddy server
- name: Create SSH config info
copy:
content: |
# SSH Configuration for git.w5isp.com
#
# Since SSH protocol doesn't support hostname-based routing like HTTP,
# users need to configure their SSH client to access git properly.
#
# Add this to your ~/.ssh/config:
#
# Host git.w5isp.com
# ProxyJump caddy.w5isp.com
# HostName 100.109.233.14
# User git
# Port 22
#
# This allows:
# - ssh caddy.w5isp.com → Direct access to caddy server
# - ssh git.w5isp.com → Access to Forgejo via ProxyJump
# - git clone git@git.w5isp.com:user/repo.git → Works with ProxyJump
dest: /etc/ssh/ssh_routing_info.txt
mode: '0644'
handlers:
- name: reload caddy
service:
name: caddy
state: reloaded

View file

@ -1,162 +0,0 @@
---
- name: Configure Caddy reverse proxy with Tailscale on Debian 13
hosts: vultr
become: yes
vars:
tailscale_key: "{{ lookup('env', 'TAILSCALE_KEY') }}"
tasks:
- name: Wait for cloud-init to complete
wait_for:
path: /var/lib/cloud/instance/boot-finished
timeout: 300
- name: Check cloud-init status
command: cloud-init status --wait
register: cloud_init_status
changed_when: false
failed_when: false
- name: Show cloud-init status
debug:
var: cloud_init_status.stdout
- name: Check if Caddy is installed
command: which caddy
register: caddy_installed
changed_when: false
failed_when: false
- name: Install Caddy if not present
apt:
name: caddy
state: present
update_cache: yes
when: caddy_installed.rc != 0
- name: Verify Caddy is installed and running
service:
name: caddy
state: started
enabled: yes
- name: Check if Tailscale is installed
command: which tailscale
register: tailscale_check
changed_when: false
failed_when: false
- name: Install Tailscale on Debian
block:
- name: Install Tailscale repository key
get_url:
url: https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg
dest: /usr/share/keyrings/tailscale-archive-keyring.gpg
- name: Add Tailscale repository
apt_repository:
repo: "deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/debian bookworm main"
state: present
filename: tailscale
- name: Update apt cache
apt:
update_cache: yes
- name: Install Tailscale
apt:
name: tailscale
state: present
- name: Enable and start Tailscale daemon
service:
name: tailscaled
state: started
enabled: yes
when:
- ansible_os_family == "Debian"
- tailscale_check.rc != 0
- name: Verify Tailscale is installed
command: tailscale version
register: tailscale_version
changed_when: false
- name: Show Tailscale version
debug:
var: tailscale_version.stdout
- name: Check if Tailscale is already authenticated
command: tailscale status --json
register: tailscale_status_check
changed_when: false
failed_when: false
- name: Restart tailscaled service
service:
name: tailscaled
state: restarted
register: restart_result
- name: Wait for tailscaled to start
pause:
seconds: 5
when: restart_result is changed
- name: Try to authenticate with explicit socket path
command: tailscale --socket=/var/run/tailscale/tailscaled.sock up --authkey="{{ tailscale_key }}" --accept-routes --hostname=caddy
when:
- tailscale_key is defined
- tailscale_key != ""
environment:
TAILSCALE_SOCKET: /var/run/tailscale/tailscaled.sock
register: tailscale_auth
failed_when: false
ignore_errors: yes
- name: Debug authentication result
debug:
msg: |
RC: {{ tailscale_auth.rc | default('N/A') }}
STDOUT: {{ tailscale_auth.stdout | default('') }}
STDERR: {{ tailscale_auth.stderr | default('') }}
when: tailscale_auth is defined and tailscale_auth is not skipped
- name: Get Tailscale status
command: tailscale --socket=/var/run/tailscale/tailscaled.sock status
register: tailscale_final_status
changed_when: false
failed_when: false
- name: Show final Tailscale status
debug:
var: tailscale_final_status.stdout_lines
when: tailscale_final_status is defined and tailscale_final_status.stdout_lines is defined
- name: Show Tailscale setup issue
debug:
msg: |
WARNING: Tailscale authentication failed.
You may need to manually authenticate Tailscale on this host.
To debug further, SSH to the host and try:
tailscale up --authkey=YOUR_KEY
when: tailscale_auth is defined and tailscale_auth is not skipped and tailscale_auth.rc != 0
- name: Update Caddyfile with any custom configurations
template:
src: Caddyfile.j2
dest: /etc/caddy/Caddyfile
owner: caddy
group: caddy
mode: "0644"
backup: yes
notify: reload caddy
when: false # Disable for now - can be enabled when you have custom configs
handlers:
- name: reload caddy
service:
name: caddy
state: reloaded

View file

@ -1,40 +0,0 @@
---
- name: Check and Install PostGIS
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Check PostgreSQL version
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -c "SELECT version();"
register: pg_version
- name: Update apt cache in container
command: pct exec {{ ct_id }} -- bash -c "apt-get update"
- name: Search for PostGIS packages
command: pct exec {{ ct_id }} -- bash -c "apt-cache search postgis | grep postgresql"
register: postgis_packages
- name: Display available packages
debug:
msg: |
PostgreSQL Version:
{{ pg_version.stdout }}
Available PostGIS packages:
{{ postgis_packages.stdout }}
- name: Install PostGIS (flexible version)
command: pct exec {{ ct_id }} -- bash -c "apt-get install -y postgis"
register: install_result
- name: Enable PostGIS in APRS database
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
- name: Display installation result
debug:
msg: "PostGIS installation completed!"

View file

@ -1,40 +0,0 @@
---
- name: Check Ceph status on Proxmox
hosts: proxmox[0]
become: true
tasks:
- name: Check Ceph status
command: ceph status
register: ceph_status
changed_when: false
- name: Display Ceph status
debug:
var: ceph_status.stdout_lines
- name: List Ceph pools
command: ceph osd pool ls
register: ceph_pools
changed_when: false
- name: Display Ceph pools
debug:
msg: "Available pools: {{ ceph_pools.stdout_lines }}"
- name: List Ceph auth users
command: ceph auth ls
register: ceph_users
changed_when: false
- name: Check if kubernetes user exists
debug:
msg: "Kubernetes user exists: {{ 'client.kubernetes' in ceph_users.stdout }}"
- name: Get Ceph config
command: ceph config generate-minimal-conf
register: ceph_conf
changed_when: false
- name: Display Ceph config
debug:
var: ceph_conf.stdout_lines

View file

@ -1,42 +0,0 @@
---
- name: Check PostgreSQL Container 300 Status
hosts: lab02
gather_facts: no
tasks:
- name: List PostgreSQL clusters
ansible.builtin.command: pct exec 300 -- pg_lsclusters
register: pg_clusters
changed_when: false
- name: Display PostgreSQL clusters
ansible.builtin.debug:
msg: "{{ pg_clusters.stdout_lines }}"
- name: Check disk space for PostgreSQL
ansible.builtin.command: pct exec 300 -- df -h /var/lib/postgresql
register: disk_space
changed_when: false
- name: Display disk space
ansible.builtin.debug:
msg: "{{ disk_space.stdout_lines }}"
- name: List databases and their sizes
ansible.builtin.command: |
pct exec 300 -- sudo -u postgres psql -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) as size FROM pg_database ORDER BY pg_database_size(datname) DESC;"
register: db_sizes
changed_when: false
- name: Display database sizes
ansible.builtin.debug:
msg: "{{ db_sizes.stdout_lines }}"
- name: Check active connections
ansible.builtin.command: |
pct exec 300 -- sudo -u postgres psql -c "SELECT datname, count(*) as connections FROM pg_stat_activity GROUP BY datname ORDER BY connections DESC;"
register: active_connections
changed_when: false
- name: Display active connections
ansible.builtin.debug:
msg: "{{ active_connections.stdout_lines }}"

View file

@ -1,29 +0,0 @@
---
- name: Check PostgreSQL configuration
hosts: proxmox
become: yes
gather_facts: no
vars:
ct_id: 300
tasks:
- name: Find PostgreSQL version and config location
shell: |
pct exec {{ ct_id }} -- bash -c "
# Check if PostgreSQL is installed
which psql || echo 'psql not found'
# Find PostgreSQL version
dpkg -l | grep postgresql-[0-9] | grep -v client || echo 'PostgreSQL package not found'
# Find pg_hba.conf location
find /etc -name pg_hba.conf 2>/dev/null || echo 'pg_hba.conf not found'
# Check if PostgreSQL is running
systemctl status postgresql --no-pager || echo 'PostgreSQL service not found'
"
register: postgres_info
- name: Display PostgreSQL information
debug:
msg: "{{ postgres_info.stdout_lines }}"

View file

@ -1,10 +0,0 @@
---
- name: Create Ceph keyfile for mounting
hosts: proxmox[0]
become: true
tasks:
- name: Create kubernetes key file
copy:
content: "AQAq14Jo/g4ZORAAwxpPOmqj1PYgmih6s6b4vA=="
dest: /etc/ceph/client.kubernetes.key
mode: '0600'

View file

@ -1,56 +0,0 @@
---
- name: Setup CephFS for Kubernetes shared storage
hosts: proxmox[0]
become: true
vars:
cephfs_name: kubernetes-shared
tasks:
- name: Check if CephFS already exists
command: ceph fs ls
register: cephfs_list
changed_when: false
- name: Display existing CephFS
debug:
var: cephfs_list.stdout_lines
- name: Create CephFS data pool if needed
command: ceph osd pool create cephfs_kubernetes_data 32
register: data_pool
failed_when:
- data_pool.rc != 0
- "'already exists' not in data_pool.stderr"
changed_when: data_pool.rc == 0
- name: Create CephFS metadata pool if needed
command: ceph osd pool create cephfs_kubernetes_metadata 16
register: metadata_pool
failed_when:
- metadata_pool.rc != 0
- "'already exists' not in metadata_pool.stderr"
changed_when: metadata_pool.rc == 0
- name: Create CephFS if needed
command: ceph fs new {{ cephfs_name }} cephfs_kubernetes_metadata cephfs_kubernetes_data
register: cephfs_create
failed_when:
- cephfs_create.rc != 0
- "'already exists' not in cephfs_create.stderr"
changed_when: cephfs_create.rc == 0
- name: Get CephFS status
command: ceph fs status {{ cephfs_name }}
register: cephfs_status
changed_when: false
failed_when: false
- name: Display CephFS status
debug:
var: cephfs_status.stdout_lines
when: cephfs_status.rc == 0
- name: Ensure kubernetes user has CephFS permissions
command: |
ceph auth caps client.kubernetes mon 'allow r' mds 'allow rw' osd 'allow rw pool=cephfs_kubernetes_data, allow rw pool=cephfs_kubernetes_metadata, allow rw pool=kubernetes'
changed_when: false

View file

@ -1,65 +0,0 @@
---
- name: Create Debian 13 Cloud-Init Template on Proxmox
hosts: lab02 # Run on first Proxmox host
gather_facts: no
vars:
template_vmid: 9000
template_name: "debian-13-cloudinit"
storage: "ceph"
tasks:
- name: Check if template already exists
command: qm status {{ template_vmid }}
register: vm_check
failed_when: false
changed_when: false
- name: Template creation block
when: vm_check.rc != 0
block:
- name: Download Debian 13 cloud image
get_url:
url: https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2
dest: /var/lib/vz/template/iso/debian-13-generic-amd64.qcow2
mode: '0644'
- name: Create VM for template
command: qm create {{ template_vmid }} --name {{ template_name }} --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-single
- name: Import disk image
command: qm importdisk {{ template_vmid }} /var/lib/vz/template/iso/debian-13-generic-amd64.qcow2 {{ storage }}
register: import_result
- name: Get imported disk name
set_fact:
imported_disk: "{{ storage }}:vm-{{ template_vmid }}-disk-0"
- name: Attach imported disk
command: qm set {{ template_vmid }} --scsi0 {{ imported_disk }}
- name: Add cloud-init drive
command: qm set {{ template_vmid }} --ide2 {{ storage }}:cloudinit
- name: Configure boot and display settings
command: qm set {{ template_vmid }} --boot c --bootdisk scsi0 --serial0 socket --vga serial0
- name: Enable QEMU guest agent
command: qm set {{ template_vmid }} --agent enabled=1
- name: Set cloud-init defaults
command: qm set {{ template_vmid }} --ipconfig0 ip=dhcp --ciuser debian
- name: Resize disk to 20G
command: qm resize {{ template_vmid }} scsi0 20G
- name: Convert to template
command: qm template {{ template_vmid }}
- name: Clean up downloaded image
file:
path: /var/lib/vz/template/iso/debian-13-generic-amd64.qcow2
state: absent
- name: Template ready
debug:
msg: "Template '{{ template_name }}' (ID: {{ template_vmid }}) is ready on {{ inventory_hostname }}"

View file

@ -1,10 +0,0 @@
---
- name: Create Forgejo storage directory
hosts: proxmox[0]
become: true
tasks:
- name: Create Forgejo directory on CephFS
file:
path: /mnt/cephfs-kubernetes/volumes/forgejo
state: directory
mode: '0777'

View file

@ -1,111 +0,0 @@
---
- name: Create MariaDB LXC Container
hosts: lab02
become: true
vars:
ct_id: 301
ct_name: mariadb
ct_template: "local:vztmpl/debian-13-standard_13.1-1_amd64.tar.zst"
ct_memory: 2048 # 2GB RAM
ct_disk_size: 50 # 50GB storage
ct_cores: 2
ct_ip: "10.0.19.6/22"
ct_gateway: "10.0.19.254"
ct_storage: "ceph" # Use Ceph RBD storage
mariadb_root_password: "dOtPgrVnkIW1zwfjvW9rSw"
tasks:
- name: Create MariaDB LXC container
command: |
pct create {{ ct_id }} {{ ct_template }} \
--hostname {{ ct_name }} \
--memory {{ ct_memory }} \
--cores {{ ct_cores }} \
--net0 name=eth0,bridge=vmbr0,ip={{ ct_ip }},gw={{ ct_gateway }} \
--storage {{ ct_storage }} \
--rootfs {{ ct_storage }}:{{ ct_disk_size }} \
--unprivileged 1 \
--features nesting=1 \
--onboot 1 \
--startup order=20
- name: Start container
command: pct start {{ ct_id }}
- name: Wait for container to boot
pause:
seconds: 30
- name: Update container packages
shell: pct exec {{ ct_id }} -- apt update && pct exec {{ ct_id }} -- apt upgrade -y
- name: Install MariaDB
shell: |
pct exec {{ ct_id }} -- bash -c "
export DEBIAN_FRONTEND=noninteractive
apt-get install -y mariadb-server mariadb-client
"
- name: Start and enable MariaDB
shell: |
pct exec {{ ct_id }} -- bash -c "
systemctl enable mariadb
systemctl start mariadb
"
- name: Configure MariaDB for network access
shell: |
pct exec {{ ct_id }} -- bash -c "
# Update bind-address
sed -i 's/^bind-address.*=.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf
# Set root password and configure access
mysql -u root << 'EOF'
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{{ mariadb_root_password }}');
CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '{{ mariadb_root_password }}';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EOF
# Restart MariaDB
systemctl restart mariadb
"
- name: Save credentials to file
copy:
content: |
MariaDB Container Credentials
=============================
Container ID: {{ ct_id }}
Host: lab02
IP Address: 10.0.19.6
Port: 3306
Root Credentials:
Username: root
Password: {{ mariadb_root_password }}
Connection String:
mysql -h 10.0.19.6 -u root -p'{{ mariadb_root_password }}'
Note: This container was created on {{ ansible_date_time.iso8601 }}
dest: "./mariadb_credentials.txt"
mode: '0600'
delegate_to: localhost
- name: Display connection info
debug:
msg: |
MariaDB container created successfully!
Container details:
- Container ID: {{ ct_id }}
- Host: lab02
- IP: 10.0.19.6
- Storage: {{ ct_disk_size }}GB on {{ ct_storage }}
Root credentials:
- Username: root
- Password: {{ mariadb_root_password }}
Credentials saved to: ./mariadb_credentials.txt

View file

@ -1,216 +0,0 @@
---
- name: Create PostgreSQL 17 LXC Container with Debian 13 on lab02
hosts: lab02
become: true
vars:
ct_id: 300
ct_name: postgres
ct_template: "local:vztmpl/debian-13-standard_13.1-1_amd64.tar.zst"
ct_memory: 8192 # 8GB RAM
ct_disk_size: 512 # 512GB storage as requested
ct_cores: 4
ct_ip: "10.0.19.5/22"
ct_gateway: "10.0.19.254"
ct_storage: "ceph" # Use Ceph RBD storage
aprs_password: "{{ lookup('password', '/dev/null chars=ascii_letters,digits length=16') }}"
tasks:
- name: Check if container already exists
command: pct status {{ ct_id }}
register: ct_exists
failed_when: false
changed_when: false
- name: Stop and destroy existing container if it exists
shell: |
pct stop {{ ct_id }} || true
pct destroy {{ ct_id }} --purge || true
when: ct_exists.rc == 0
- name: List available templates
command: pveam list local
register: template_list
changed_when: false
- name: Show available templates
debug:
msg: "Available templates: {{ template_list.stdout_lines | select('match', '.*debian.*') | list }}"
- name: Create PostgreSQL LXC container
command: |
pct create {{ ct_id }} {{ ct_template }} \
--hostname {{ ct_name }} \
--memory {{ ct_memory }} \
--cores {{ ct_cores }} \
--net0 name=eth0,bridge=vmbr0,ip={{ ct_ip }},gw={{ ct_gateway }} \
--storage {{ ct_storage }} \
--rootfs {{ ct_storage }}:{{ ct_disk_size }} \
--unprivileged 1 \
--features nesting=1 \
--onboot 1 \
--startup order=10
- name: Start container
command: pct start {{ ct_id }}
- name: Wait for container to boot
pause:
seconds: 30
- name: Update container packages
shell: pct exec {{ ct_id }} -- apt update && pct exec {{ ct_id }} -- apt upgrade -y
- name: Install PostgreSQL 17 repository
shell: |
pct exec {{ ct_id }} -- bash -c "
apt-get install -y wget ca-certificates
sh -c 'echo \"deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main\" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
apt-get update
"
- name: Install PostgreSQL 17 and PostGIS
shell: |
pct exec {{ ct_id }} -- bash -c "
export DEBIAN_FRONTEND=noninteractive
apt-get install -y postgresql-17 postgresql-contrib-17 postgresql-17-postgis-3 postgis
"
- name: Start and enable PostgreSQL
shell: |
pct exec {{ ct_id }} -- bash -c "
systemctl enable postgresql
systemctl start postgresql
"
- name: Configure PostgreSQL for network access
shell: |
pct exec {{ ct_id }} -- bash -c "
# Configure postgresql.conf
sed -i \"s/#listen_addresses = 'localhost'/listen_addresses = '*'/g\" /etc/postgresql/17/main/postgresql.conf
# Configure pg_hba.conf
echo '# TYPE DATABASE USER ADDRESS METHOD' > /etc/postgresql/17/main/pg_hba.conf
echo 'local all postgres peer' >> /etc/postgresql/17/main/pg_hba.conf
echo 'local all all peer' >> /etc/postgresql/17/main/pg_hba.conf
echo 'host all all 127.0.0.1/32 scram-sha-256' >> /etc/postgresql/17/main/pg_hba.conf
echo 'host all all ::1/128 scram-sha-256' >> /etc/postgresql/17/main/pg_hba.conf
echo 'host all all 10.0.19.0/24 scram-sha-256' >> /etc/postgresql/17/main/pg_hba.conf
echo 'host all all 10.0.16.0/22 scram-sha-256' >> /etc/postgresql/17/main/pg_hba.conf
echo 'host all all 10.42.0.0/16 scram-sha-256' >> /etc/postgresql/17/main/pg_hba.conf
echo 'host all all 10.43.0.0/16 scram-sha-256' >> /etc/postgresql/17/main/pg_hba.conf
# Restart PostgreSQL
systemctl restart postgresql
"
- name: Create APRS user and database
shell: |
pct exec {{ ct_id }} -- su - postgres -c "psql << 'EOF'
-- Create APRS user and database
CREATE USER aprs WITH PASSWORD '{{ aprs_password }}';
CREATE DATABASE aprs OWNER aprs;
\c aprs
CREATE EXTENSION IF NOT EXISTS postgis;
GRANT ALL PRIVILEGES ON DATABASE aprs TO aprs;
GRANT ALL ON SCHEMA public TO aprs;
ALTER DATABASE aprs SET search_path TO public, postgis;
EOF"
- name: Test APRS database connection
shell: |
pct exec {{ ct_id }} -- su - postgres -c "psql -d aprs -c 'SELECT version(); SELECT PostGIS_full_version();'"
register: db_test
- name: Save APRS password to file
copy:
content: "{{ aprs_password }}"
dest: "./aprs_db_password.txt"
mode: '0600'
delegate_to: localhost
- name: Display connection info
debug:
msg: |
PostgreSQL 17 with PostGIS installed successfully on Debian 13!
Container details:
- Container ID: {{ ct_id }}
- Host: lab02
- IP: 10.0.19.5
- Storage: {{ ct_disk_size }}GB on {{ ct_storage }}
Database details:
- PostgreSQL version: 17
- PostGIS: Installed
- Database: aprs
- User: aprs
- Password: {{ aprs_password }}
- Password saved to: ./aprs_db_password.txt
Connection string:
postgresql://aprs:{{ aprs_password }}@10.0.19.5:5432/aprs
Test output:
{{ db_test.stdout }}
- name: Create K8s secret for APRS database
hosts: home-k3s-control-1
become: true
vars:
aprs_password: "{{ hostvars['lab02']['aprs_password'] }}"
tasks:
- name: Create aprs namespace if it doesn't exist
command: |
/usr/local/bin/k3s kubectl create namespace aprs --dry-run=client -o yaml | /usr/local/bin/k3s kubectl apply -f -
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
- name: Delete existing database secret if it exists
command: |
/usr/local/bin/k3s kubectl delete secret aprs-db-secret -n aprs --ignore-not-found=true
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
- name: Create database secret
command: |
/usr/local/bin/k3s kubectl create secret generic aprs-db-secret \
--from-literal=DATABASE_URL="ecto://aprs:{{ aprs_password }}@10.0.19.5:5432/aprs" \
--from-literal=DB_HOST="10.0.19.5" \
--from-literal=DB_PORT="5432" \
--from-literal=DB_NAME="aprs" \
--from-literal=DB_USER="aprs" \
--from-literal=DB_PASSWORD="{{ aprs_password }}" \
-n aprs
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
- name: Verify secret was created
command: |
/usr/local/bin/k3s kubectl get secret aprs-db-secret -n aprs -o yaml
register: secret_verify
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
- name: Display secret creation result
debug:
msg: |
Kubernetes secret created successfully!
Secret name: aprs-db-secret
Namespace: aprs
To use in your deployment, add:
envFrom:
- secretRef:
name: aprs-db-secret
Or individual env vars:
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: aprs-db-secret
key: DATABASE_URL

View file

@ -1,63 +0,0 @@
---
- name: Create PostgreSQL LXC Container on Proxmox
hosts: proxmox[0]
become: true
vars:
ct_id: 300
ct_name: postgres
ct_template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
ct_memory: 8192 # 8GB RAM
ct_disk_size: 1024 # 1TB storage
ct_cores: 4
ct_ip: "10.0.19.5/22"
ct_gateway: "10.0.19.254"
ct_storage: "ceph" # Use Ceph storage
tasks:
- name: Check if container already exists
command: pct status {{ ct_id }}
register: ct_exists
failed_when: false
changed_when: false
- name: Create PostgreSQL LXC container
command: |
pct create {{ ct_id }} {{ ct_template }} \
--hostname {{ ct_name }} \
--memory {{ ct_memory }} \
--cores {{ ct_cores }} \
--net0 name=eth0,bridge=vmbr0,ip={{ ct_ip }},gw={{ ct_gateway }} \
--storage {{ ct_storage }} \
--rootfs {{ ct_storage }}:{{ ct_disk_size }} \
--unprivileged 1 \
--features nesting=1 \
--onboot 1 \
--startup order=10
when: ct_exists.rc != 0
- name: Start container
command: pct start {{ ct_id }}
when: ct_exists.rc != 0
- name: Wait for container to be ready
wait_for:
host: 10.0.19.5
port: 22
timeout: 60
delay: 10
when: ct_exists.rc != 0
- name: Update container packages
shell: pct exec {{ ct_id }} -- apt update && pct exec {{ ct_id }} -- apt upgrade -y
when: ct_exists.rc != 0
- name: Display container info
debug:
msg: |
PostgreSQL LXC Container created:
- ID: {{ ct_id }}
- Name: {{ ct_name }}
- IP: 10.0.19.5
- RAM: {{ ct_memory }}MB
- Storage: {{ ct_disk_size }}GB
- Cores: {{ ct_cores }}

View file

@ -1,12 +0,0 @@
---
- name: Create PostgreSQL storage directory
hosts: proxmox[0]
become: true
tasks:
- name: Create PostgreSQL directory on CephFS
file:
path: /mnt/cephfs-kubernetes/volumes/postgresql
state: directory
mode: '0700'
owner: '999' # PostgreSQL container user
group: '999'

View file

@ -1,44 +0,0 @@
---
- name: Create RBD volumes on Proxmox Ceph
hosts: proxmox[0]
become: true
vars:
volumes:
- name: adguard-config
size: 2G
- name: adguard-data
size: 10G
- name: nodered-data
size: 5G
tasks:
- name: Create RBD volumes
shell: |
rbd create kubernetes/{{ item.name }} --size {{ item.size }} --image-feature layering
loop: "{{ volumes }}"
register: rbd_create
failed_when:
- rbd_create.rc != 0
- "'already exists' not in rbd_create.stderr"
changed_when: rbd_create.rc == 0
- name: List created volumes
command: rbd ls kubernetes
register: rbd_list
changed_when: false
- name: Display volumes
debug:
msg: "Created volumes in kubernetes pool: {{ rbd_list.stdout_lines }}"
- name: Get volume info
shell: |
rbd info kubernetes/{{ item.name }} --format json
loop: "{{ volumes }}"
register: volume_info
changed_when: false
- name: Display volume details
debug:
msg: "Volume {{ item.item.name }}: {{ (item.stdout | from_json).size | int / 1024 / 1024 / 1024 }}GB"
loop: "{{ volume_info.results }}"

View file

@ -1,138 +0,0 @@
---
- name: Deploy Ceph CSI for direct RBD access
hosts: localhost
connection: local
vars:
kubeconfig: "{{ playbook_dir }}/kubeconfig"
ceph_csi_dir: "../cluster/ceph-csi"
tasks:
- name: Create temporary directory for fixed configs
tempfile:
state: directory
register: temp_dir
- name: Copy original kustomization.yaml
copy:
src: "{{ ceph_csi_dir }}/kustomization.yaml"
dest: "{{ temp_dir.path }}/kustomization.yaml"
- name: Update kustomization to use fixed files
replace:
path: "{{ temp_dir.path }}/kustomization.yaml"
regexp: "{{ item.old }}"
replace: "{{ item.new }}"
loop:
- { old: "csi-rbdplugin.yaml", new: "csi-rbdplugin-fixed.yaml" }
- { old: "csi-rbdplugin-provisioner.yaml", new: "csi-rbdplugin-provisioner-fixed.yaml" }
- { old: "ceph-config.yaml", new: "ceph-config-fixed.yaml" }
- name: Copy all required files to temp directory
copy:
src: "{{ ceph_csi_dir }}/{{ item }}"
dest: "{{ temp_dir.path }}/{{ item }}"
loop:
- namespace.yaml
- ceph-csi-configmap.yaml
- ceph-csi-secret.yaml
- ceph-csi-rbd-driver.yaml
- csi-driver.yaml
- ceph-storageclass.yaml
- csi-rbdplugin-fixed.yaml
- csi-rbdplugin-provisioner-fixed.yaml
- ceph-config-fixed.yaml
ignore_errors: yes
- name: Check if namespace exists
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl get namespace ceph-csi
register: ns_check
failed_when: false
changed_when: false
- name: Delete existing Ceph CSI deployment if exists
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl delete daemonset,deployment,service -n ceph-csi -l app in (csi-rbdplugin,csi-rbdplugin-provisioner,csi-metrics) --ignore-not-found
when: ns_check.rc == 0
- name: Wait for pods to terminate
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl wait --for=delete pod -n ceph-csi -l app in (csi-rbdplugin,csi-rbdplugin-provisioner) --timeout=60s || true
when: ns_check.rc == 0
- name: Apply Ceph CSI configuration
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl apply -k {{ temp_dir.path }}
register: apply_result
- name: Display apply result
debug:
var: apply_result.stdout_lines
- name: Wait for CSI pods to be ready
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl wait --for=condition=ready pod -n ceph-csi -l app=csi-rbdplugin-provisioner --timeout=300s || true
kubectl wait --for=condition=ready pod -n ceph-csi -l app=csi-rbdplugin --timeout=300s || true
register: wait_result
ignore_errors: yes
- name: Check CSI pod status
shell: |
export KUBECONFIG={{ kubeconfig }}
echo "=== CSI Provisioner Pods ==="
kubectl get pods -n ceph-csi -l app=csi-rbdplugin-provisioner
echo -e "\n=== CSI Node Plugin Pods ==="
kubectl get pods -n ceph-csi -l app=csi-rbdplugin
echo -e "\n=== Storage Class ==="
kubectl get storageclass ceph-rbd
register: status_check
- name: Display CSI status
debug:
var: status_check.stdout_lines
- name: Clean up temporary directory
file:
path: "{{ temp_dir.path }}"
state: absent
- name: Test Ceph CSI with a sample PVC
shell: |
export KUBECONFIG={{ kubeconfig }}
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-ceph-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: ceph-rbd
resources:
requests:
storage: 1Gi
EOF
register: test_pvc
- name: Check test PVC status
shell: |
export KUBECONFIG={{ kubeconfig }}
sleep 5
kubectl get pvc test-ceph-pvc
register: pvc_status
- name: Display test PVC status
debug:
var: pvc_status.stdout_lines
- name: Clean up test PVC
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl delete pvc test-ceph-pvc --ignore-not-found
when: test_pvc is succeeded

View file

@ -1,102 +0,0 @@
---
- name: Deploy MariaDB LXC Container on Proxmox
hosts: proxmox
gather_facts: no
vars:
container_id: 301
container_hostname: mariadb
container_template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
container_cores: 2
container_memory: 4096
container_storage: 50
container_ip: "10.0.19.6/24"
container_gateway: "10.0.19.254"
container_nameserver: "9.9.9.9"
container_password: "{{ lookup('password', '/dev/null length=20 chars=ascii_letters,digits') }}"
tasks:
- name: Check if container already exists
shell: pct status {{ container_id }}
register: container_status
ignore_errors: yes
changed_when: false
- name: Create MariaDB LXC container
shell: |
pct create {{ container_id }} {{ container_template }} \
--hostname {{ container_hostname }} \
--cores {{ container_cores }} \
--memory {{ container_memory }} \
--storage ceph \
--rootfs ceph:{{ container_storage }} \
--net0 name=eth0,bridge=vmbr0,ip={{ container_ip }},gw={{ container_gateway }} \
--nameserver {{ container_nameserver }} \
--password {{ container_password }} \
--unprivileged 1 \
--features nesting=1 \
--ostype debian
when: container_status.rc != 0
- name: Start the container
shell: pct start {{ container_id }}
when: container_status.rc != 0
- name: Wait for container to be ready
pause:
seconds: 30
when: container_status.rc != 0
- name: Update container and install MariaDB
shell: |
pct exec {{ container_id }} -- bash -c "
apt-get update
apt-get install -y mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
"
- name: Secure MariaDB installation
shell: |
pct exec {{ container_id }} -- bash -c "
mysql -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY 'mariadb_root_2024';\"
mysql -uroot -pmariadb_root_2024 -e \"DELETE FROM mysql.user WHERE User='';\"
mysql -uroot -pmariadb_root_2024 -e \"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');\"
mysql -uroot -pmariadb_root_2024 -e \"DROP DATABASE IF EXISTS test;\"
mysql -uroot -pmariadb_root_2024 -e \"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';\"
mysql -uroot -pmariadb_root_2024 -e \"FLUSH PRIVILEGES;\"
"
- name: Configure MariaDB for network access
shell: |
pct exec {{ container_id }} -- bash -c "
sed -i 's/^bind-address.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf
systemctl restart mariadb
"
- name: Configure firewall
shell: |
pct exec {{ container_id }} -- bash -c "
apt-get install -y ufw
ufw allow 3306/tcp
ufw --force enable
"
- name: Create Ghost database and user
shell: |
pct exec {{ container_id }} -- bash -c "
mysql -uroot -pmariadb_root_2024 -e \"CREATE DATABASE IF NOT EXISTS ghost CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\"
mysql -uroot -pmariadb_root_2024 -e \"CREATE USER IF NOT EXISTS 'ghost'@'%' IDENTIFIED BY 'ghost_db_password_2024';\"
mysql -uroot -pmariadb_root_2024 -e \"GRANT ALL PRIVILEGES ON ghost.* TO 'ghost'@'%';\"
mysql -uroot -pmariadb_root_2024 -e \"FLUSH PRIVILEGES;\"
"
- name: Display container information
debug:
msg:
- "MariaDB container created successfully!"
- "Container ID: {{ container_id }}"
- "IP Address: 10.0.19.6"
- "Root password: mariadb_root_2024"
- "Ghost database: ghost"
- "Ghost user: ghost"
- "Ghost password: ghost_db_password_2024"

View file

@ -1,25 +0,0 @@
---
- name: Download Debian 13 LXC template
hosts: proxmox[0]
become: true
tasks:
- name: Update template list
command: pveam update
changed_when: false
- name: Search for Debian 13 template
command: pveam available | grep -i "debian-13"
register: debian13_search
changed_when: false
failed_when: false
- name: Show available Debian 13 templates
debug:
var: debian13_search.stdout_lines
when: debian13_search.rc == 0
- name: Download Debian 12 template (if 13 not available)
command: pveam download local debian-12-standard_12.7-1_amd64.tar.zst
register: download_result
failed_when: false
changed_when: "'already exists' not in download_result.stderr"

View file

@ -1,33 +0,0 @@
---
- name: Enable PostGIS Extension
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Try to enable PostGIS extension
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
register: postgis_enable
ignore_errors: yes
- name: Install PostGIS for PostgreSQL 15 if extension failed
command: pct exec {{ ct_id }} -- bash -c "apt-get install -y postgresql-15-postgis-3 || apt-get install -y postgis postgresql-postgis"
when: postgis_enable.rc != 0
register: install_result
ignore_errors: yes
- name: Try to enable PostGIS again if it was just installed
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
when: postgis_enable.rc != 0
- name: Show results
debug:
msg: |
{% if postgis_enable.rc == 0 %}
PostGIS extension enabled successfully!
{% else %}
PostGIS installation attempt: {{ install_result.stderr | default('') }}
{% endif %}

View file

@ -1,45 +0,0 @@
---
- name: Enable PostGIS in APRS Database
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Check if PostGIS is installed
command: |
pct exec {{ ct_id }} -- dpkg -l | grep postgis
register: postgis_check
ignore_errors: yes
- name: Install PostGIS if not present
command: pct exec {{ ct_id }} -- apt-get install -y postgresql-16-postgis-3 postgresql-16-postgis-3-scripts
when: postgis_check.rc != 0
- name: Enable PostGIS extension in APRS database
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
register: postgis_result
- name: Enable PostGIS topology extension
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;"
ignore_errors: yes
- name: Verify PostGIS installation
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "SELECT PostGIS_Version();"
register: postgis_version
ignore_errors: yes
- name: Display results
debug:
msg: |
PostGIS setup completed!
Extension creation: {{ postgis_result.stdout | default('Done') }}
{% if postgis_version.rc == 0 %}
PostGIS Version: {{ postgis_version.stdout }}
{% else %}
Note: PostGIS may require package installation
{% endif %}

View file

@ -1,77 +0,0 @@
---
- name: Fix NFS permissions for all users write access
hosts: proxmox[0]
become: true
vars:
nfs_export_path: /mnt/cephfs-kubernetes
k8s_network: "10.0.19.0/24"
tasks:
- name: Update NFS export configuration for better access
lineinfile:
path: /etc/exports
line: "{{ nfs_export_path }} {{ k8s_network }}(rw,sync,no_subtree_check,no_root_squash,no_all_squash)"
regexp: "^{{ nfs_export_path | regex_escape() }}\\s+"
create: yes
backup: yes
notify:
- restart nfs
- export nfs shares
- name: Ensure base directories have proper permissions
file:
path: "{{ nfs_export_path }}/{{ item }}"
state: directory
mode: '0777'
loop:
- volumes
- shared
- name: Set world-writable permissions on general storage directories
file:
path: "{{ nfs_export_path }}/{{ item }}"
state: directory
mode: '0777'
loop:
- volumes/adguard
- volumes/nodered
- volumes/forgejo
- shared
ignore_errors: yes # In case some don't exist yet
- name: Preserve PostgreSQL specific permissions
file:
path: "{{ nfs_export_path }}/volumes/postgresql"
state: directory
owner: '999'
group: '999'
mode: '0700'
recurse: yes
when: ansible_check_mode or (nfs_export_path + "/volumes/postgresql") is directory
ignore_errors: yes
- name: Set sticky bit on shared directories
command: chmod +t {{ nfs_export_path }}/{{ item }}
loop:
- volumes
- shared
changed_when: false
- name: Display current exports
command: exportfs -v
register: exports_output
changed_when: false
- name: Show current exports
debug:
var: exports_output.stdout_lines
handlers:
- name: restart nfs
service:
name: nfs-kernel-server
state: restarted
- name: export nfs shares
command: exportfs -ra
changed_when: false

View file

@ -1,33 +0,0 @@
---
- name: Fix PostgreSQL pg_hba.conf for K3s access
hosts: proxmox
become: yes
gather_facts: no
vars:
ct_id: 300
tasks:
- name: Update pg_hba.conf to allow K3s nodes
shell: |
pct exec {{ ct_id }} -- bash -c "
# Backup current pg_hba.conf
cp /etc/postgresql/16/main/pg_hba.conf /etc/postgresql/16/main/pg_hba.conf.backup
# Check if K3s network entry exists
if ! grep -q '10.0.19.0/24' /etc/postgresql/16/main/pg_hba.conf; then
# Add K3s network access
echo 'host all all 10.0.19.0/24 md5' >> /etc/postgresql/16/main/pg_hba.conf
fi
# Reload PostgreSQL
systemctl reload postgresql
"
- name: Verify pg_hba.conf entries
shell: |
pct exec {{ ct_id }} -- grep '10.0.19' /etc/postgresql/16/main/pg_hba.conf
register: hba_entries
- name: Display pg_hba.conf entries
debug:
msg: "{{ hba_entries.stdout_lines }}"

View file

@ -1,17 +0,0 @@
---
- name: Fix PostgreSQL directory permissions
hosts: proxmox[0]
become: true
tasks:
- name: Fix PostgreSQL directory ownership
file:
path: /mnt/cephfs-kubernetes/volumes/postgresql
state: directory
owner: '999'
group: '999'
mode: '0700'
recurse: yes
- name: Ensure write permissions
command: chmod -R 700 /mnt/cephfs-kubernetes/volumes/postgresql
changed_when: false

View file

@ -1,23 +0,0 @@
Forgejo Database Credentials
============================
PostgreSQL Container: 300 on lab02
Host: 10.0.19.5
Port: 5432
Database: forgejo
Username: forgejo
Password: 6Cj1BR1zgutXn0LzDCvanw
Connection String:
postgresql://forgejo:6Cj1BR1zgutXn0LzDCvanw@10.0.19.5:5432/forgejo
For Forgejo app.ini configuration:
[database]
DB_TYPE = postgres
HOST = 10.0.19.5:5432
NAME = forgejo
USER = forgejo
PASSWD = 6Cj1BR1zgutXn0LzDCvanw
SSL_MODE = disable
Note: The password has also been saved to /tmp/forgejo_db_password.txt

View file

@ -1,22 +0,0 @@
---
- name: Get Ceph kubernetes user key
hosts: proxmox[0]
become: true
tasks:
- name: Get kubernetes user auth
command: ceph auth get client.kubernetes
register: k8s_auth
changed_when: false
- name: Display kubernetes auth
debug:
var: k8s_auth.stdout_lines
- name: Extract just the key
command: ceph auth print-key client.kubernetes
register: k8s_key
changed_when: false
- name: Display key
debug:
msg: "Kubernetes key: {{ k8s_key.stdout }}"

View file

@ -1,44 +0,0 @@
---
- name: Install Ceph client packages on K3s nodes
hosts: k3s_cluster
become: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Install ceph-common package
package:
name: ceph-common
state: present
register: ceph_install
ignore_errors: yes
- name: Check if ceph-common is installed
command: which ceph
register: ceph_check
failed_when: false
changed_when: false
- name: Display ceph installation status
debug:
msg: "Ceph client installed: {{ ceph_check.rc == 0 }}"
- name: Create ceph configuration directory
file:
path: /etc/ceph
state: directory
mode: '0755'
- name: Load RBD kernel module
modprobe:
name: rbd
state: present
- name: Persist RBD module
lineinfile:
path: /etc/modules-load.d/ceph.conf
line: rbd
create: yes

View file

@ -1,9 +0,0 @@
---
- name: Install NFS client on K3s nodes
hosts: k3s_cluster
become: true
tasks:
- name: Install NFS client packages
package:
name: nfs-common
state: present

View file

@ -1,35 +0,0 @@
---
- name: Install PostGIS in PostgreSQL LXC
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Update apt cache
command: pct exec {{ ct_id }} -- apt-get update
- name: Install PostGIS package
command: pct exec {{ ct_id }} -- apt-get install -y postgresql-16-postgis-3
- name: Enable PostGIS extension in APRS database
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
- name: Enable PostGIS topology extension
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;"
- name: Verify PostGIS installation
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "SELECT PostGIS_Version();"
register: postgis_version
- name: Display PostGIS version
debug:
msg: |
PostGIS has been installed successfully!
Version: {{ postgis_version.stdout }}
The APRS database now has spatial capabilities for location data.

View file

@ -1,58 +0,0 @@
all:
children:
proxmox:
hosts:
lab02:
ansible_host: 10.0.16.231
lab03:
ansible_host: 10.0.16.232
lab04:
ansible_host: 10.0.16.233
vars:
ansible_user: root
ansible_python_interpreter: /usr/bin/python3
k3s_cluster:
children:
k3s_control:
hosts:
home-k3s-control-1:
ansible_host: 10.0.19.100
k3s_control_node: true
home-k3s-control-2:
ansible_host: 10.0.19.101
k3s_control_node: true
home-k3s-control-3:
ansible_host: 10.0.19.102
k3s_control_node: true
k3s_workers:
hosts:
home-k3s-worker-1:
ansible_host: 10.0.19.110
home-k3s-worker-2:
ansible_host: 10.0.19.111
home-k3s-worker-3:
ansible_host: 10.0.19.112
vars:
ansible_user: debian
ansible_ssh_private_key_file: "~/.ssh/id_rsa"
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
k3s_version: v1.28.5+k3s1
caddy_servers:
hosts:
caddy:
ansible_host: 204.110.191.212
vars:
ansible_user: ansible # Now using ansible user after bootstrap
ansible_ssh_private_key_file: "~/.ssh/ansible"
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
postgresql:
hosts:
postgres-lxc:
ansible_host: 10.0.19.5
vars:
ansible_user: root # LXC containers typically use root
ansible_ssh_private_key_file: "~/.ssh/id_rsa" # Try default key
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"

View file

@ -1,19 +0,0 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTlRnek9EUXdOakV3SGhjTk1qVXdPVEl3TVRZd01UQXhXaGNOTXpVd09URTRNVFl3TVRBeApXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTlRnek9EUXdOakV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFUMUwxRHNza3prWmd5VGVuY1lISjhrdTJkZXF0WmF0VWJzbm1DVFRobHQKSWN1a3VKRkhNTENtZzN3VzdqM3BjRU90Z3Q0TUVUMWV4ZmRpNGt6ZUNiZU9vMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVS9DMkVzSWsxSVlYVm4xWkpvbVVFClorQitHU3d3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnSVlpdHZrMkQyQ0dSWkoybmJHZlhnL1AvVkcyVnNhNmsKT1crNVpGZ1VuTDhDSUVKZFphWWg3M3I3bnN6aGp4Nmk4NXlzVFEydUxHMkdUWllHOHlQanJlaXYKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://100.102.235.20:6443
name: default
contexts:
- context:
cluster: default
user: default
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJZko4aDRBSlc4MXN3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOelU0TXpnME1EWXhNQjRYRFRJMU1Ea3lNREUyTURFd01Wb1hEVEkyTURreQpNREUyTURFd01Wb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJEZjRwOE1BQ1JGaS9jTnUKa3QxRFoyb0tjVDlMeEM0aGtPeWZBdlJqUVRhQUFBN2Z6L3ZYdFJTd2dkN0JpMjlHMEF2WlViQ201anNoUGRRZwpEdVdMRksyalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCVEt2Y3FLMnZyQmM4b1pzay80SkhRWUdSN2ZKVEFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlCZ2M0WDZqSmhBNzRpZ1VTZFNBanc0Yks1d29SVUFXLzFwMkNwaThvbXJnQUloQUxBQ2tBdWU3dmo2aFc0ZwpqNUhZL2VhMWN0RjVjUERycjB4NllsYWpXWDJFCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTlRnek9EUXdOakV3SGhjTk1qVXdPVEl3TVRZd01UQXhXaGNOTXpVd09URTRNVFl3TVRBeApXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTlRnek9EUXdOakV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFScjR0a1IzZGUzOEhnTUxlNjBRYURyUjA2SmdCRHNRNng5cXp0dVVpNU8KSVpkekVwVWdCMHB6aUw1YkJ3eFVkVDVHTk5yUEt0OWlNVy9PMi9FQWgxc1pvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXlyM0tpdHI2d1hQS0diSlArQ1IwCkdCa2UzeVV3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnRmMrVVhselRMUWluRzM0bHlzeGdSK2JOTFhFS1RaS2gKbTBpV0dkZUhPRGNDSUNGdXZDdWxLWEx1TjFLNjkrT0dHNnhoUnNyRzAyUmY3TDJsVlZsekp4dDcKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5tTDRiZW5hQllDb3FZSGUwU2dWbzVaQ2hrQnNaQVZGOHYwQU5VeE9jOUpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFTi9pbnd3QUpFV0w5dzI2UzNVTm5hZ3B4UDB2RUxpR1E3SjhDOUdOQk5vQUFEdC9QKzllMQpGTENCM3NHTGIwYlFDOWxSc0tibU95RTkxQ0FPNVlzVXJRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=

View file

@ -1,19 +0,0 @@
apiVersion: v1
clusters:
- cluster:
insecure-skip-tls-verify: true
server: https://100.102.235.20:6443
name: default
contexts:
- context:
cluster: default
user: default
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrRENDQVRlZ0F3SUJBZ0lJWHliZTEvMDh1N2N3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOelUzTURnMU9EUTRNQjRYRFRJMU1Ea3dOVEUxTWpRd09Gb1hEVEkyTURrdwpOVEUxTWpRd09Gb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJOWEs5VndoYWNaT1BjNVQKVGp2bVdCdDJaZmxiWmkzM1pjZk5VWVRTSGUxbzZaMEhvUVhHdDN5V2lrOVdmUGF6OTRYSHZVZXdLSmpOTnJBWgpsYzk2U21HalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCVE5CbDhSUGJnRTBFVXRUeGJyUXhyekE0Lzg2VEFLQmdncWhrak9QUVFEQWdOSEFEQkUKQWlBcmUySmNPa0lDbmhZWXptODZCVThEampNdFJZUTE1SWRoamJuenZPZ09WUUlnREtsaHIxL1FKWkMwellaLwoveUt5OEEzTWN4aXRQMEJkdXBvQzhnRDdrQkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTlRjd09EVTRORGd3SGhjTk1qVXdPVEExTVRVeU5EQTRXaGNOTXpVd09UQXpNVFV5TkRBNApXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTlRjd09EVTRORGd3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFUNlZCdUI4UStFblNXOUFZT0N3b0ZsUFcwdzdqUjZlZC9qdTArbVIySDgKVkdnTk1qdXFUalU0a3o0RWhrYmJsaHloVVc5ZE9FWVRJSDM2bVlGUHltaVBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXpRWmZFVDI0Qk5CRkxVOFc2ME1hCjh3T1AvT2t3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQVBja200cHA2anI1aStWcFRLbDNFTU5EWmNoQTZ5eXgKVDA4UCt5eHlieGxyQWlCZWJ6cDFqSDBYbUZKVjcxWjd2dDZFMzlsVTcyTTJJdlM0YW1zUncvd1lqdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFPNjA5Q2tBNEl1NXQyRFdMVkhBMHRDTFBZSHlBZ21nQlBrU3V2OElXOUVvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFMWNyMVhDRnB4azQ5emxOT08rWllHM1psK1Z0bUxmZGx4ODFSaE5JZDdXanBuUWVoQmNhMwpmSmFLVDFaODlyUDNoY2U5UjdBb21NMDJzQm1WejNwS1lRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=

View file

@ -1,13 +0,0 @@
---
- name: List available LXC templates
hosts: proxmox[0]
become: true
tasks:
- name: List templates in local storage
command: pveam list local
register: templates
changed_when: false
- name: Show Debian templates
debug:
msg: "{{ templates.stdout_lines | select('search', 'debian') | list }}"

View file

@ -1,23 +0,0 @@
MariaDB Container Credentials
=============================
Created: 2025-09-12
Container Details:
- Container ID: 301
- Host: lab02
- IP Address: 10.0.19.6
- Port: 3306
Root Credentials:
- Username: root
- Password: dOtPgrVnkIW1zwfjvW9rSw
- Connection: mysql -h 10.0.19.6 -u root -p'dOtPgrVnkIW1zwfjvW9rSw'
Wavelog Database:
- Database: wavelog
- Username: wavelog
- Password: m4icNmGTRtOMgrNxsxEdrw
- Connection: mysql -h 10.0.19.6 -u wavelog -p'm4icNmGTRtOMgrNxsxEdrw' wavelog
Kubernetes Secret:
kubectl create secret generic wavelog-db-secret --from-literal=password="m4icNmGTRtOMgrNxsxEdrw" -n wavelog

View file

@ -1,28 +0,0 @@
---
- name: Migrate template to all Proxmox nodes
hosts: lab02
gather_facts: no
vars:
template_vmid: 9000
tasks:
- name: Migrate template to lab03
command: qm migrate {{ template_vmid }} lab03
register: migrate_lab03
failed_when:
- migrate_lab03.rc != 0
- "'already on node' not in migrate_lab03.stderr"
- name: Migrate template to lab04
command: qm migrate {{ template_vmid }} lab04
register: migrate_lab04
failed_when:
- migrate_lab04.rc != 0
- "'already on node' not in migrate_lab04.stderr"
- name: Migrate template back to lab02
command: qm migrate {{ template_vmid }} lab02
register: migrate_back
failed_when:
- migrate_back.rc != 0
- "'already on node' not in migrate_back.stderr"

View file

@ -1,40 +0,0 @@
---
- name: Configure Ceph RBD access on K3s nodes
hosts: k3s_cluster
become: true
vars:
ceph_monitors: "10.0.16.231:6789,10.0.16.232:6789,10.0.16.233:6789"
ceph_user: "kubernetes"
ceph_key: "AQAq14Jo/g4ZORAAwxpPOmqj1PYgmih6s6b4vA=="
tasks:
- name: Create Ceph configuration file
copy:
content: |
[global]
fsid = 6e5e8932-3613-471d-afa0-410fade4d4ff
mon_host = 10.0.16.231,10.0.16.232,10.0.16.233
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx
dest: /etc/ceph/ceph.conf
mode: '0644'
- name: Create Ceph keyring for kubernetes user
copy:
content: |
[client.kubernetes]
key = {{ ceph_key }}
dest: /etc/ceph/ceph.client.kubernetes.keyring
mode: '0600'
- name: Test Ceph connectivity
shell: |
rbd ls kubernetes --id kubernetes
register: rbd_test
failed_when: false
changed_when: false
- name: Show RBD test result
debug:
var: rbd_test

View file

@ -1,73 +0,0 @@
---
- name: Pre-upgrade checks for PostgreSQL via Proxmox
hosts: lab02 # Proxmox host
become: yes
gather_facts: no
vars:
container_id: 300
tasks:
- name: Check if container is running
command: pct status {{ container_id }}
register: container_status
changed_when: false
- name: Display container status
debug:
var: container_status.stdout
- name: Check current PostgreSQL version
command: pct exec {{ container_id }} -- sudo -u postgres psql -t -c "SELECT version();"
register: pg_version
changed_when: false
- name: Display current version
debug:
msg: "Current version: {{ pg_version.stdout }}"
- name: List PostgreSQL clusters
command: pct exec {{ container_id }} -- pg_lsclusters
register: pg_clusters
changed_when: false
- name: Display clusters
debug:
var: pg_clusters.stdout_lines
- name: Check disk space in container
command: pct exec {{ container_id }} -- df -h /var/lib/postgresql
register: disk_space
changed_when: false
- name: Display disk space
debug:
var: disk_space.stdout_lines
- name: List installed PostgreSQL packages
command: pct exec {{ container_id }} -- dpkg -l | grep postgresql
register: pg_packages
changed_when: false
- name: Display PostgreSQL packages
debug:
var: pg_packages.stdout_lines
- name: Get database sizes
command: |
pct exec {{ container_id }} -- sudo -u postgres psql -t -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database WHERE datname NOT IN ('template0', 'template1') ORDER BY pg_database_size(pg_database.datname) DESC;"
register: db_sizes
changed_when: false
- name: Display database sizes
debug:
var: db_sizes.stdout_lines
- name: Check active connections
command: |
pct exec {{ container_id }} -- sudo -u postgres psql -t -c "SELECT count(*) as connections, datname FROM pg_stat_activity GROUP BY datname ORDER BY connections DESC;"
register: active_connections
changed_when: false
- name: Display active connections
debug:
var: active_connections.stdout_lines

View file

@ -1,85 +0,0 @@
---
- name: Pre-upgrade checks for PostgreSQL
hosts: postgresql
become: yes
gather_facts: yes
tasks:
- name: Check current PostgreSQL version
command: sudo -u postgres psql -t -c "SELECT version();"
register: pg_version
changed_when: false
- name: Display current version
debug:
msg: "Current version: {{ pg_version.stdout }}"
- name: Check PostgreSQL service status
command: systemctl status postgresql
register: pg_status
changed_when: false
- name: List PostgreSQL clusters
command: pg_lsclusters
register: pg_clusters
changed_when: false
- name: Display clusters
debug:
var: pg_clusters.stdout_lines
- name: Get database sizes
become_user: postgres
shell: |
psql -t -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database WHERE datname NOT IN ('template0', 'template1') ORDER BY pg_database_size(pg_database.datname) DESC;"
register: db_sizes
changed_when: false
- name: Display database sizes
debug:
msg: "Database sizes:"
- name: Show database sizes
debug:
var: db_sizes.stdout_lines
- name: Check disk space
command: df -h /var/lib/postgresql
register: disk_space
changed_when: false
- name: Display disk space
debug:
var: disk_space.stdout_lines
- name: List installed PostgreSQL packages
shell: dpkg -l | grep postgresql | grep -E "^ii"
register: pg_packages
changed_when: false
- name: Display PostgreSQL packages
debug:
var: pg_packages.stdout_lines
- name: Check active connections
become_user: postgres
shell: |
psql -t -c "SELECT count(*) as connections, datname FROM pg_stat_activity GROUP BY datname ORDER BY connections DESC;"
register: active_connections
changed_when: false
- name: Display active connections
debug:
var: active_connections.stdout_lines
- name: Check for replication slots (if any)
become_user: postgres
command: psql -t -c "SELECT * FROM pg_replication_slots;"
register: replication_slots
changed_when: false
ignore_errors: yes
- name: Display replication slots
debug:
var: replication_slots.stdout_lines
when: replication_slots.rc == 0

View file

@ -1,173 +0,0 @@
---
- name: Upgrade PostgreSQL from 15 to 17
hosts: postgresql
become: yes
gather_facts: yes
vars:
old_version: 15
new_version: 17
postgres_data_dir: "/var/lib/postgresql"
backup_dir: "/var/backups/postgresql-upgrade-{{ ansible_date_time.epoch }}"
tasks:
- name: Check current PostgreSQL version
command: sudo -u postgres psql -t -c "SELECT version();"
register: pg_version_check
changed_when: false
- name: Display current PostgreSQL version
debug:
msg: "Current PostgreSQL version: {{ pg_version_check.stdout }}"
- name: Install required packages
apt:
name:
- postgresql-{{ new_version }}
- postgresql-client-{{ new_version }}
- postgresql-contrib-{{ new_version }}
- postgresql-{{ new_version }}-postgis-3 # If PostGIS is needed
state: present
update_cache: yes
- name: Stop PostgreSQL services
service:
name: "{{ item }}"
state: stopped
loop:
- "postgresql@{{ old_version }}-main"
- "postgresql@{{ new_version }}-main"
ignore_errors: yes
- name: Create backup directory
file:
path: "{{ backup_dir }}"
state: directory
owner: postgres
group: postgres
mode: '0755'
- name: Get list of databases
become_user: postgres
postgresql_info:
filter: databases
register: pg_databases
- name: Backup all databases
become_user: postgres
command: |
pg_dumpall -p 5432 > {{ backup_dir }}/all_databases.sql
environment:
PGVERSION: "{{ old_version }}"
- name: Backup PostgreSQL configuration files
copy:
src: "{{ item }}"
dest: "{{ backup_dir }}/"
remote_src: yes
owner: postgres
group: postgres
loop:
- "/etc/postgresql/{{ old_version }}/main/postgresql.conf"
- "/etc/postgresql/{{ old_version }}/main/pg_hba.conf"
ignore_errors: yes
- name: Check if new cluster exists
stat:
path: "/etc/postgresql/{{ new_version }}/main"
register: new_cluster_check
- name: Drop new cluster if it exists
command: pg_dropcluster {{ new_version }} main --stop
when: new_cluster_check.stat.exists
ignore_errors: yes
- name: Upgrade PostgreSQL cluster
command: |
pg_upgradecluster {{ old_version }} main
register: upgrade_result
environment:
LC_ALL: C.UTF-8
LANG: C.UTF-8
- name: Show upgrade result
debug:
var: upgrade_result.stdout_lines
- name: Start new PostgreSQL service
service:
name: "postgresql@{{ new_version }}-main"
state: started
enabled: yes
- name: Verify new PostgreSQL version
command: sudo -u postgres psql -p 5432 -t -c "SELECT version();"
register: new_pg_version
changed_when: false
- name: Display new PostgreSQL version
debug:
msg: "New PostgreSQL version: {{ new_pg_version.stdout }}"
- name: Update PostgreSQL configuration
lineinfile:
path: "/etc/postgresql/{{ new_version }}/main/postgresql.conf"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^#?listen_addresses', line: "listen_addresses = '*'" }
- { regexp: '^#?max_connections', line: "max_connections = 200" }
- { regexp: '^#?shared_buffers', line: "shared_buffers = 2GB" }
- { regexp: '^#?effective_cache_size', line: "effective_cache_size = 6GB" }
- { regexp: '^#?work_mem', line: "work_mem = 16MB" }
notify: restart postgresql
- name: Update pg_hba.conf for network access
blockinfile:
path: "/etc/postgresql/{{ new_version }}/main/pg_hba.conf"
block: |
# Allow connections from K3s cluster
host all all 10.0.19.0/24 scram-sha-256
host all all 10.0.0.0/8 scram-sha-256
marker: "# {mark} ANSIBLE MANAGED BLOCK - K3s access"
notify: restart postgresql
- name: Check if PostGIS extension needs update
become_user: postgres
command: psql -d forgejo -c "SELECT PostGIS_version();"
register: postgis_check
ignore_errors: yes
changed_when: false
- name: Update PostGIS extension if present
become_user: postgres
command: psql -d {{ item }} -c "ALTER EXTENSION postgis UPDATE;"
loop:
- forgejo
- postgres
when: postgis_check.rc == 0
ignore_errors: yes
- name: Run ANALYZE on all databases
become_user: postgres
command: psql -d {{ item }} -c "ANALYZE;"
loop: "{{ pg_databases.databases.keys() | select('ne', 'template0') | list }}"
when: pg_databases.databases is defined
ignore_errors: yes
- name: Display upgrade summary
debug:
msg:
- "PostgreSQL upgrade completed successfully!"
- "Old version: PostgreSQL {{ old_version }}"
- "New version: PostgreSQL {{ new_version }}"
- "Backup location: {{ backup_dir }}"
- ""
- "IMPORTANT: The old cluster is still present but stopped."
- "After verifying everything works correctly, you can remove it with:"
- " sudo pg_dropcluster {{ old_version }} main"
handlers:
- name: restart postgresql
service:
name: "postgresql@{{ new_version }}-main"
state: restarted

View file

@ -1,184 +0,0 @@
# Manual PostgreSQL 15 to 17 Upgrade Steps
## Prerequisites
- SSH into Proxmox host: `ssh root@10.0.16.231` (or appropriate host)
- Access PostgreSQL container: `pct enter 300`
## Step 1: Pre-upgrade Checks
```bash
# Check current version
sudo -u postgres psql -t -c "SELECT version();"
# List current clusters
pg_lsclusters
# Check disk space
df -h /var/lib/postgresql
# List databases and sizes
sudo -u postgres psql -t -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database WHERE datname NOT IN ('template0', 'template1') ORDER BY pg_database_size(pg_database.datname) DESC;"
# Check active connections
sudo -u postgres psql -t -c "SELECT count(*) as connections, datname FROM pg_stat_activity GROUP BY datname ORDER BY connections DESC;"
```
## Step 2: Backup Current Data
```bash
# Create backup directory
mkdir -p /var/backups/postgresql-upgrade-$(date +%s)
cd /var/backups/postgresql-upgrade-$(date +%s)
# Backup all databases
sudo -u postgres pg_dumpall > all_databases.sql
# Backup configuration files
cp /etc/postgresql/15/main/postgresql.conf postgresql.conf.backup
cp /etc/postgresql/15/main/pg_hba.conf pg_hba.conf.backup
# Note important settings from postgresql.conf
grep -E "listen_addresses|max_connections|shared_buffers|effective_cache_size|work_mem" /etc/postgresql/15/main/postgresql.conf
```
## Step 3: Install PostgreSQL 17
```bash
# Add PostgreSQL APT repository
apt-get update
apt-get install -y wget ca-certificates
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt/ bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list
apt-get update
# Install PostgreSQL 17
apt-get install -y postgresql-17 postgresql-client-17 postgresql-contrib-17
# If using PostGIS
apt-get install -y postgresql-17-postgis-3
```
## Step 4: Stop Services
```bash
# Stop all PostgreSQL services
systemctl stop postgresql
# Verify services are stopped
systemctl status postgresql
```
## Step 5: Perform the Upgrade
```bash
# Check clusters again
pg_lsclusters
# Drop the empty PostgreSQL 17 cluster (if it was auto-created)
pg_dropcluster 17 main --stop
# Upgrade the cluster
pg_upgradecluster 15 main
# This will:
# - Create new cluster with PostgreSQL 17
# - Migrate all data
# - Update ports (old cluster moves to 5433, new stays on 5432)
```
## Step 6: Configure PostgreSQL 17
```bash
# Edit postgresql.conf
nano /etc/postgresql/17/main/postgresql.conf
# Update these settings:
# listen_addresses = '*'
# max_connections = 200
# shared_buffers = 2GB
# effective_cache_size = 6GB
# work_mem = 16MB
# Edit pg_hba.conf for network access
nano /etc/postgresql/17/main/pg_hba.conf
# Add these lines:
# host all all 10.0.19.0/24 scram-sha-256
# host all all 10.0.0.0/8 scram-sha-256
```
## Step 7: Start and Verify
```bash
# Start PostgreSQL 17
systemctl start postgresql@17-main
systemctl enable postgresql@17-main
# Check status
systemctl status postgresql@17-main
# Verify version
sudo -u postgres psql -p 5432 -t -c "SELECT version();"
# Check clusters
pg_lsclusters
# Test connection
sudo -u postgres psql -l
```
## Step 8: Post-upgrade Tasks
```bash
# Update PostGIS extension (if used)
sudo -u postgres psql -d forgejo -c "ALTER EXTENSION postgis UPDATE;"
# Run ANALYZE on all databases for optimizer statistics
for db in $(sudo -u postgres psql -t -c "SELECT datname FROM pg_database WHERE datname NOT IN ('template0', 'template1');"); do
echo "Analyzing $db..."
sudo -u postgres psql -d $db -c 'ANALYZE;'
done
# Test remote connectivity
# From another host:
# psql -h 10.0.19.5 -U forgejo -d forgejo
```
## Step 9: Cleanup (After Verification)
```bash
# Once everything is confirmed working:
# Stop old cluster
systemctl stop postgresql@15-main
# Optional: Remove old cluster (only after thorough testing!)
# pg_dropcluster 15 main
# Remove old packages (only after thorough testing!)
# apt-get remove postgresql-15 postgresql-client-15 postgresql-contrib-15
```
## Important Notes
- The old PostgreSQL 15 cluster will remain on port 5433
- The new PostgreSQL 17 cluster will be on port 5432
- Ensure all applications are tested before removing the old cluster
- Keep backups for at least 30 days after upgrade
- Monitor logs: `tail -f /var/log/postgresql/postgresql-17-main.log`
## Rollback Plan
If issues occur:
```bash
# Stop PostgreSQL 17
systemctl stop postgresql@17-main
# Change PostgreSQL 15 back to port 5432
sed -i 's/port = 5433/port = 5432/' /etc/postgresql/15/main/postgresql.conf
# Start PostgreSQL 15
systemctl start postgresql@15-main
# Update applications to point back to PostgreSQL 15
```

View file

@ -1,129 +0,0 @@
---
- name: Upgrade PostgreSQL from 15 to 17 via Proxmox
hosts: lab02 # Proxmox host where the LXC container runs
become: yes
gather_facts: yes
vars:
container_id: 300
old_version: 15
new_version: 17
backup_dir: "/var/backups/postgresql-upgrade-{{ ansible_date_time.epoch }}"
tasks:
- name: Check current PostgreSQL version
command: pct exec {{ container_id }} -- sudo -u postgres psql -t -c "SELECT version();"
register: pg_version_check
changed_when: false
- name: Display current PostgreSQL version
debug:
msg: "Current PostgreSQL version: {{ pg_version_check.stdout }}"
- name: Update package lists in container
command: pct exec {{ container_id }} -- apt-get update
changed_when: false
- name: Install PostgreSQL 17 repository
command: |
pct exec {{ container_id }} -- bash -c "
apt-get install -y wget ca-certificates &&
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - &&
echo 'deb http://apt.postgresql.org/pub/repos/apt/ bookworm-pgdg main' > /etc/apt/sources.list.d/pgdg.list &&
apt-get update
"
- name: Install PostgreSQL 17 packages
command: |
pct exec {{ container_id }} -- apt-get install -y postgresql-17 postgresql-client-17 postgresql-contrib-17 postgresql-17-postgis-3
- name: Stop PostgreSQL services
command: pct exec {{ container_id }} -- systemctl stop postgresql
- name: Create backup directory
command: pct exec {{ container_id }} -- mkdir -p {{ backup_dir }}
- name: Set ownership of backup directory
command: pct exec {{ container_id }} -- chown postgres:postgres {{ backup_dir }}
- name: Backup all databases
command: |
pct exec {{ container_id }} -- sudo -u postgres pg_dumpall -p 5432 > {{ container_id }}_postgres_backup.sql
delegate_to: localhost
- name: Copy backup into container
command: |
pct push {{ container_id }} {{ container_id }}_postgres_backup.sql {{ backup_dir }}/all_databases.sql
- name: List current clusters
command: pct exec {{ container_id }} -- pg_lsclusters
register: clusters_before
changed_when: false
- name: Display current clusters
debug:
var: clusters_before.stdout_lines
- name: Drop PostgreSQL 17 cluster if exists
command: pct exec {{ container_id }} -- pg_dropcluster {{ new_version }} main --stop
ignore_errors: yes
- name: Upgrade PostgreSQL cluster
command: pct exec {{ container_id }} -- pg_upgradecluster {{ old_version }} main
register: upgrade_result
- name: Show upgrade result
debug:
var: upgrade_result.stdout_lines
- name: Start PostgreSQL 17
command: pct exec {{ container_id }} -- systemctl start postgresql@{{ new_version }}-main
- name: Enable PostgreSQL 17
command: pct exec {{ container_id }} -- systemctl enable postgresql@{{ new_version }}-main
- name: Verify new PostgreSQL version
command: pct exec {{ container_id }} -- sudo -u postgres psql -p 5432 -t -c "SELECT version();"
register: new_pg_version
changed_when: false
- name: Display new PostgreSQL version
debug:
msg: "New PostgreSQL version: {{ new_pg_version.stdout }}"
- name: Update PostgreSQL configuration for network access
command: |
pct exec {{ container_id }} -- bash -c "
sed -i \"s/^#listen_addresses.*/listen_addresses = '*'/\" /etc/postgresql/{{ new_version }}/main/postgresql.conf &&
echo 'host all all 10.0.19.0/24 scram-sha-256' >> /etc/postgresql/{{ new_version }}/main/pg_hba.conf &&
echo 'host all all 10.0.0.0/8 scram-sha-256' >> /etc/postgresql/{{ new_version }}/main/pg_hba.conf
"
- name: Restart PostgreSQL to apply configuration
command: pct exec {{ container_id }} -- systemctl restart postgresql@{{ new_version }}-main
- name: Run ANALYZE on all databases
command: |
pct exec {{ container_id }} -- bash -c "
for db in \$(sudo -u postgres psql -t -c \"SELECT datname FROM pg_database WHERE datname NOT IN ('template0', 'template1');\"); do
sudo -u postgres psql -d \$db -c 'ANALYZE;'
done
"
ignore_errors: yes
- name: Clean up local backup file
file:
path: "{{ container_id }}_postgres_backup.sql"
state: absent
delegate_to: localhost
- name: Display upgrade summary
debug:
msg:
- "PostgreSQL upgrade completed successfully!"
- "Old version: PostgreSQL {{ old_version }}"
- "New version: PostgreSQL {{ new_version }}"
- "Backup location in container: {{ backup_dir }}"
- ""
- "IMPORTANT: The old cluster is still present but stopped."
- "After verifying everything works correctly, you can remove it with:"
- " pct exec {{ container_id }} -- pg_dropcluster {{ old_version }} main"

View file

@ -1,161 +0,0 @@
---
- name: Recreate K3s Worker-2 VM on Proxmox
hosts: lab02
gather_facts: no
vars:
template_vmid: 9000
network_gateway: "10.0.19.254"
ssh_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHLyVSEEAEbGZZqwPwEup0XrlTpu3x3iF9ExvvQPA4xN graham@mcintire.me"
worker2:
vmid: 1201
name: "home-k3s-worker-2"
ip: "10.0.19.111/24"
cores: 4
memory: 8192
disk: "64G"
target_node: "lab03"
tasks:
- name: Clone template to create worker-2
command: |
qm clone {{ template_vmid }} {{ worker2.vmid }} \
--name {{ worker2.name }} \
--full true \
--target {{ worker2.target_node }}
register: clone_result
- name: Configure worker-2 VM
delegate_to: lab03
command: |
qm set {{ worker2.vmid }} \
--cores {{ worker2.cores }} \
--memory {{ worker2.memory }} \
--ipconfig0 ip={{ worker2.ip }},gw={{ network_gateway }} \
--sshkeys /root/.ssh/authorized_keys \
--onboot 1
- name: Resize disk if needed
delegate_to: lab03
command: |
qm resize {{ worker2.vmid }} scsi0 {{ worker2.disk }}
ignore_errors: yes
- name: Start worker-2 VM
delegate_to: lab03
command: qm start {{ worker2.vmid }}
- name: Wait for VM to boot
pause:
seconds: 90
- name: Test SSH connectivity
delegate_to: localhost
wait_for:
host: 10.0.19.111
port: 22
timeout: 300
- name: Configure K3s on worker-2
hosts: home-k3s-worker-2
become: true
tasks:
- name: Wait for SSH to be ready
wait_for_connection:
delay: 30
timeout: 300
- name: Gather facts
setup:
- name: Install curl
package:
name: curl
state: present
- name: Download k3s binary
get_url:
url: https://github.com/k3s-io/k3s/releases/download/v1.28.5+k3s1/k3s
dest: /usr/local/bin/k3s
mode: '0755'
- name: Create k3s config directory
file:
path: /etc/rancher/k3s
state: directory
mode: '0755'
- name: Get token from control node
delegate_to: home-k3s-control-1
slurp:
src: /var/lib/rancher/k3s/server/node-token
register: node_token
- name: Create k3s agent config
copy:
content: |
server: https://10.0.19.100:6443
token: {{ node_token.content | b64decode | trim }}
with-node-id: true
dest: /etc/rancher/k3s/config.yaml
- name: Create k3s-agent systemd service
copy:
content: |
[Unit]
Description=Lightweight Kubernetes
Documentation=https://k3s.io
Wants=network-online.target
After=network-online.target
[Service]
Type=notify
EnvironmentFile=-/etc/default/%N
EnvironmentFile=-/etc/sysconfig/%N
KillMode=process
Delegate=yes
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Restart=always
RestartSec=5s
ExecStartPre=/bin/sh -xc '! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service'
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/k3s agent
[Install]
WantedBy=multi-user.target
dest: /etc/systemd/system/k3s-agent.service
- name: Start k3s-agent service
systemd:
name: k3s-agent
daemon_reload: yes
enabled: yes
state: started
- name: Verify worker-2 joined cluster
hosts: home-k3s-control-1
become: true
tasks:
- name: Wait for worker-2 to join
shell: /usr/local/bin/k3s kubectl get nodes | grep -E "home-k3s-worker-2.*Ready"
register: worker2_status
until: worker2_status.rc == 0
retries: 30
delay: 10
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
- name: Display cluster status
command: /usr/local/bin/k3s kubectl get nodes
register: final_status
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
- name: Show cluster status
debug:
var: final_status.stdout_lines

View file

@ -1,27 +0,0 @@
---
- name: Replicate Debian 13 template to all Proxmox nodes
hosts: proxmox
gather_facts: no
vars:
template_vmid: 9000
storage: "ceph"
source_node: "lab02"
tasks:
- name: Check if template exists on this node
command: qm status {{ template_vmid }}
register: vm_check
failed_when: false
changed_when: false
- name: Clone template from source node
when:
- vm_check.rc != 0
- inventory_hostname != source_node
command: |
qm clone {{ template_vmid }} {{ template_vmid }} --name debian-13-cloudinit --target {{ inventory_hostname }}
delegate_to: "{{ source_node }}"
- name: Template status
debug:
msg: "Template {{ template_vmid }} exists on {{ inventory_hostname }}"

View file

@ -1,50 +0,0 @@
---
- name: Setup APRS Database in PostgreSQL LXC
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Create APRS database and user
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -c "CREATE USER aprs WITH PASSWORD 'xK9mP2vQ7sR3nL5w';"
ignore_errors: yes
register: create_user
- name: Create APRS database
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -c "CREATE DATABASE aprs OWNER aprs;"
ignore_errors: yes
register: create_db
- name: Grant privileges on APRS database
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "GRANT ALL PRIVILEGES ON DATABASE aprs TO aprs; GRANT ALL ON SCHEMA public TO aprs;"
- name: Test APRS database connection
command: |
pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "SELECT current_database(), current_user;"
register: db_test
- name: Display results
debug:
msg: |
APRS database setup completed!
User creation: {{ 'Created' if create_user.rc == 0 else 'Already exists or failed' }}
Database creation: {{ 'Created' if create_db.rc == 0 else 'Already exists or failed' }}
Connection details:
- Host: 10.0.19.5
- Port: 5432
- Database: aprs
- User: aprs
- Password: xK9mP2vQ7sR3nL5w
Connection test output:
{{ db_test.stdout }}
Connection strings:
- PostgreSQL: postgresql://aprs:xK9mP2vQ7sR3nL5w@10.0.19.5:5432/aprs
- Ecto: ecto://aprs:xK9mP2vQ7sR3nL5w@10.0.19.5:5432/aprs

View file

@ -1,155 +0,0 @@
---
# This playbook sets up direct Ceph RBD access for Kubernetes
# It replaces NFS-based access with native Ceph RBD CSI
- name: Ensure Ceph kubernetes pool exists
hosts: proxmox[0]
become: true
tasks:
- name: Check if kubernetes pool exists
command: ceph osd pool ls
register: ceph_pools
changed_when: false
- name: Create kubernetes pool if not exists
command: ceph osd pool create kubernetes 128 128
when: "'kubernetes' not in ceph_pools.stdout"
- name: Initialize pool for RBD use
command: rbd pool init kubernetes
when: "'kubernetes' not in ceph_pools.stdout"
- name: Check if kubernetes user exists
command: ceph auth list
register: ceph_users
changed_when: false
- name: Create kubernetes user if not exists
shell: |
ceph auth get-or-create client.kubernetes \
mon 'profile rbd' \
osd 'profile rbd pool=kubernetes' \
mgr 'profile rbd pool=kubernetes'
when: "'client.kubernetes' not in ceph_users.stdout"
- name: Get kubernetes user key
command: ceph auth print-key client.kubernetes
register: k8s_key
changed_when: false
- name: Display kubernetes key
debug:
msg: "Kubernetes key: {{ k8s_key.stdout }}"
- name: Set fact for kubernetes key
set_fact:
ceph_kubernetes_key: "{{ k8s_key.stdout }}"
- name: Setup Ceph client on K3s nodes
import_playbook: setup-ceph-clients-k3s.yml
- name: Deploy Ceph CSI
import_playbook: deploy-ceph-csi.yml
- name: Verify Ceph RBD setup
hosts: localhost
connection: local
vars:
kubeconfig: "{{ playbook_dir }}/kubeconfig"
tasks:
- name: Check CSI driver status
shell: |
export KUBECONFIG={{ kubeconfig }}
echo "=== CSI Drivers ==="
kubectl get csidrivers
echo -e "\n=== CSI Nodes ==="
kubectl get csinodes
echo -e "\n=== Storage Classes ==="
kubectl get storageclass
echo -e "\n=== CSI Pods Status ==="
kubectl get pods -n ceph-csi
register: csi_status
- name: Display CSI status
debug:
var: csi_status.stdout_lines
- name: Create test application with Ceph RBD
shell: |
export KUBECONFIG={{ kubeconfig }}
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: ceph-test
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-rbd-pvc
namespace: ceph-test
spec:
accessModes:
- ReadWriteOnce
storageClassName: ceph-rbd
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
name: test-rbd-pod
namespace: ceph-test
spec:
containers:
- name: test
image: nginx:alpine
volumeMounts:
- name: test-volume
mountPath: /data
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: test-rbd-pvc
EOF
register: test_app
- name: Wait for test pod to be ready
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl wait --for=condition=ready pod -n ceph-test test-rbd-pod --timeout=120s
register: wait_result
ignore_errors: yes
- name: Check test application status
shell: |
export KUBECONFIG={{ kubeconfig }}
echo "=== Test PVC Status ==="
kubectl get pvc -n ceph-test
echo -e "\n=== Test Pod Status ==="
kubectl get pod -n ceph-test
echo -e "\n=== RBD Image List ==="
kubectl exec -n ceph-test test-rbd-pod -- df -h /data
register: test_status
when: wait_result is succeeded
- name: Display test results
debug:
var: test_status.stdout_lines
when: wait_result is succeeded
- name: Cleanup test resources
shell: |
export KUBECONFIG={{ kubeconfig }}
kubectl delete namespace ceph-test --ignore-not-found
when: test_app is succeeded
- name: Summary
debug:
msg:
- "Ceph RBD CSI setup complete!"
- "Storage class 'ceph-rbd' is now available for direct Ceph RBD access"
- "You can now create PVCs with storageClassName: ceph-rbd"
- "This provides better performance than NFS-based access"

View file

@ -1,123 +0,0 @@
---
- name: Setup MariaDB in Container
hosts: lab02
become: true
vars:
ct_id: 301
mariadb_root_password: "dOtPgrVnkIW1zwfjvW9rSw"
wavelog_password: "m4icNmGTRtOMgrNxsxEdrw"
tasks:
- name: Configure DNS in container
shell: |
pct exec {{ ct_id }} -- bash -c "echo 'nameserver 9.9.9.9' > /etc/resolv.conf"
- name: Update container packages
shell: pct exec {{ ct_id }} -- apt-get update
register: apt_update
retries: 3
delay: 5
until: apt_update.rc == 0
- name: Install MariaDB Server
shell: |
pct exec {{ ct_id }} -- bash -c "
export DEBIAN_FRONTEND=noninteractive
apt-get install -y mariadb-server mariadb-client
"
- name: Start and enable MariaDB
shell: |
pct exec {{ ct_id }} -- systemctl enable mariadb
pct exec {{ ct_id }} -- systemctl start mariadb
- name: Secure MariaDB installation
shell: |
pct exec {{ ct_id }} -- bash -c "mysql -u root << 'EOF'
-- Set root password
ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ mariadb_root_password }}';
-- Create root user for remote access
CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '{{ mariadb_root_password }}';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
-- Remove anonymous users
DELETE FROM mysql.user WHERE User='';
-- Remove test database
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
-- Reload privileges
FLUSH PRIVILEGES;
EOF"
- name: Configure MariaDB for network access
shell: |
pct exec {{ ct_id }} -- sed -i 's/^bind-address.*=.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf
pct exec {{ ct_id }} -- systemctl restart mariadb
- name: Create Wavelog database and user
shell: |
pct exec {{ ct_id }} -- mysql -u root -p'{{ mariadb_root_password }}' << 'EOF'
CREATE DATABASE IF NOT EXISTS wavelog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'wavelog'@'%' IDENTIFIED BY '{{ wavelog_password }}';
GRANT ALL PRIVILEGES ON wavelog.* TO 'wavelog'@'%';
FLUSH PRIVILEGES;
EOF
- name: Test database connections
shell: |
pct exec {{ ct_id }} -- mysql -u root -p'{{ mariadb_root_password }}' -e "SELECT VERSION();"
pct exec {{ ct_id }} -- mysql -u wavelog -p'{{ wavelog_password }}' wavelog -e "SELECT DATABASE();"
register: db_test
- name: Save all credentials
copy:
content: |
MariaDB Container Credentials
=============================
Created: {{ ansible_date_time.iso8601 }}
Container Details:
- Container ID: {{ ct_id }}
- Host: lab02
- IP Address: 10.0.19.6
- Port: 3306
Root Credentials:
- Username: root
- Password: {{ mariadb_root_password }}
- Connection: mysql -h 10.0.19.6 -u root -p'{{ mariadb_root_password }}'
Wavelog Database:
- Database: wavelog
- Username: wavelog
- Password: {{ wavelog_password }}
- Connection: mysql -h 10.0.19.6 -u wavelog -p'{{ wavelog_password }}' wavelog
Test Results:
{{ db_test.stdout }}
dest: "./mariadb_all_credentials.txt"
mode: '0600'
delegate_to: localhost
- name: Update Kubernetes secret for Wavelog
shell: |
kubectl create secret generic wavelog-db-secret \
--from-literal=password="{{ wavelog_password }}" \
--dry-run=client -o yaml | kubectl apply -f - -n wavelog
environment:
KUBECONFIG: "{{ playbook_dir }}/kubeconfig"
delegate_to: localhost
- name: Display summary
debug:
msg: |
MariaDB setup completed successfully!
Root password: {{ mariadb_root_password }}
Wavelog password: {{ wavelog_password }}
All credentials saved to: ./mariadb_all_credentials.txt
Kubernetes secret updated: wavelog-db-secret

View file

@ -1,66 +0,0 @@
---
- name: Setup NFS export for CephFS
hosts: proxmox[0]
become: true
vars:
nfs_export_path: /mnt/cephfs-kubernetes
k8s_network: "10.0.19.0/24"
tasks:
- name: Install NFS server
apt:
name:
- nfs-kernel-server
- ceph-fuse
state: present
update_cache: yes
- name: Create CephFS mount point
file:
path: "{{ nfs_export_path }}"
state: directory
mode: '0755'
- name: Mount CephFS
mount:
path: "{{ nfs_export_path }}"
src: "10.0.16.231,10.0.16.232,10.0.16.233:/"
fstype: ceph
opts: "name=kubernetes,secretfile=/etc/ceph/client.kubernetes.key"
state: mounted
- name: Create Kubernetes storage directories
file:
path: "{{ nfs_export_path }}/{{ item }}"
state: directory
mode: '0777'
owner: nobody
group: nogroup
loop:
- volumes
- volumes/adguard
- volumes/nodered
- shared
- name: Ensure world-writable permissions on CephFS mount
file:
path: "{{ nfs_export_path }}"
mode: '0777'
recurse: yes
- name: Configure NFS exports
lineinfile:
path: /etc/exports
line: "{{ nfs_export_path }} {{ k8s_network }}(rw,sync,no_subtree_check,no_root_squash,no_all_squash)"
create: yes
notify: restart nfs
- name: Export NFS shares
command: exportfs -ra
changed_when: false
handlers:
- name: restart nfs
service:
name: nfs-kernel-server
state: restarted

View file

@ -1,175 +0,0 @@
---
- name: Setup PostgreSQL with PostGIS in LXC
hosts: proxmox[0]
become: true
vars:
ct_id: 300
postgres_version: 16
tasks:
- name: Check if container is running
command: pct status {{ ct_id }}
register: ct_status
failed_when: false
changed_when: false
- name: Start container if stopped
command: pct start {{ ct_id }}
when: ct_status.rc == 0 and 'stopped' in ct_status.stdout
- name: Wait for container to be ready
wait_for:
host: 10.0.19.5
port: 22
timeout: 60
delay: 5
when: ct_status.rc == 0
- name: Check if PostgreSQL is already installed
shell: pct exec {{ ct_id }} -- which psql || echo "not installed"
register: psql_check
changed_when: false
when: ct_status.rc == 0
- name: Update package list
shell: pct exec {{ ct_id }} -- apt-get update -y
when: ct_status.rc == 0 and "'not installed' in psql_check.stdout"
- name: Install PostgreSQL
shell: pct exec {{ ct_id }} -- bash -c "export DEBIAN_FRONTEND=noninteractive && apt-get install -y postgresql postgresql-contrib"
when: ct_status.rc == 0 and "'not installed' in psql_check.stdout"
- name: Start PostgreSQL service
shell: |
pct exec {{ ct_id }} -- bash -c "
systemctl enable postgresql
systemctl start postgresql
systemctl status postgresql
"
register: pg_status
when: ct_status.rc == 0
- name: Show PostgreSQL status
debug:
var: pg_status.stdout_lines
when: pg_status is defined
- name: Get PostgreSQL version
shell: pct exec {{ ct_id }} -- ls /etc/postgresql/
register: pg_dir
when: ct_status.rc == 0
- name: Configure PostgreSQL for network access
shell: |
pct exec {{ ct_id }} -- bash -c "
# Get actual PostgreSQL version
PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | head -1)
# If no PostgreSQL directory found, exit
if [ -z \"\$PG_VERSION\" ]; then
echo 'PostgreSQL not found'
exit 1
fi
# Configure postgresql.conf
sed -i \"s/#listen_addresses = 'localhost'/listen_addresses = '*'/g\" /etc/postgresql/\$PG_VERSION/main/postgresql.conf
# Configure pg_hba.conf properly
echo '# TYPE DATABASE USER ADDRESS METHOD' > /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'local all postgres peer' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'local all all peer' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'host all all 127.0.0.1/32 scram-sha-256' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'host all all ::1/128 scram-sha-256' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'host all all 10.0.19.0/24 md5' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'host all all 10.0.16.0/22 md5' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
echo 'host all all 10.42.0.0/16 md5' >> /etc/postgresql/\$PG_VERSION/main/pg_hba.conf
# Restart PostgreSQL
systemctl restart postgresql
"
register: pg_config_result
- name: Verify pg_hba.conf was updated
shell: |
pct exec {{ ct_id }} -- bash -c "
PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | head -1)
if [ -n \"\$PG_VERSION\" ]; then
echo '=== PostgreSQL pg_hba.conf ==='
cat /etc/postgresql/\$PG_VERSION/main/pg_hba.conf | grep -E '10.0.19|10.42|10.0.16'
else
echo 'PostgreSQL not found'
fi
"
register: pg_hba_verify
- name: Display pg_hba.conf entries
debug:
var: pg_hba_verify.stdout_lines
when: pg_hba_verify is defined
- name: Install sudo if missing
shell: pct exec {{ ct_id }} -- apt-get install -y sudo
when: ct_status.rc == 0
- name: Create databases and users
shell: |
pct exec {{ ct_id }} -- su - postgres -c "psql << 'EOF'
-- Drop existing if needed and recreate
DROP DATABASE IF EXISTS forgejo;
DROP USER IF EXISTS forgejo;
CREATE USER forgejo WITH PASSWORD 'fj8K3n2Qp9Lx5mW7';
CREATE DATABASE forgejo OWNER forgejo;
\c forgejo
CREATE EXTENSION IF NOT EXISTS postgis;
GRANT ALL PRIVILEGES ON DATABASE forgejo TO forgejo;
-- Create other databases as needed
DROP DATABASE IF EXISTS apps;
DROP USER IF EXISTS appuser;
CREATE USER appuser WITH PASSWORD 'AppPass2024!';
CREATE DATABASE apps OWNER appuser;
\c apps
CREATE EXTENSION IF NOT EXISTS postgis;
GRANT ALL PRIVILEGES ON DATABASE apps TO appuser;
EOF"
when: ct_status.rc == 0
- name: Configure firewall
shell: |
pct exec {{ ct_id }} -- bash -c "
apt-get install -y ufw
ufw allow 22/tcp
ufw allow 5432/tcp
ufw --force enable
"
when: ct_status.rc == 0
- name: Wait for PostgreSQL to be ready
wait_for:
host: 10.0.19.5
port: 5432
timeout: 30
delay: 2
- name: Test PostgreSQL connectivity
shell: |
pct exec {{ ct_id }} -- sudo -u postgres psql -c "SELECT version();" || echo "PostgreSQL query failed"
register: pg_test
when: ct_status.rc == 0
- name: Display connection info
debug:
msg: |
PostgreSQL with PostGIS installed successfully!
Connection details:
- Host: 10.0.19.5
- Port: 5432
- Version: PostgreSQL {{ postgres_version }} with PostGIS 3
Databases:
- forgejo (user: forgejo, pass: fj8K3n2Qp9Lx5mW7)
- apps (user: appuser, pass: AppPass2024!)
From Kubernetes pods, connect using:
postgresql://forgejo:fj8K3n2Qp9Lx5mW7@10.0.19.5:5432/forgejo

View file

@ -1,47 +0,0 @@
---
- name: Setup Wavelog Database in MariaDB
hosts: lab02
become: true
vars:
mysql_container_id: 301
wavelog_password: "m4icNmGTRtOMgrNxsxEdrw"
tasks:
- name: Create Wavelog database and user
shell: |
pct exec {{ mysql_container_id }} -- bash -c "mysql -u root <<EOF
CREATE DATABASE IF NOT EXISTS wavelog;
CREATE USER IF NOT EXISTS 'wavelog'@'%' IDENTIFIED BY '{{ wavelog_password }}';
GRANT ALL PRIVILEGES ON wavelog.* TO 'wavelog'@'%';
FLUSH PRIVILEGES;
EOF"
register: db_creation
ignore_errors: yes
- name: If root needs password, try with socket authentication
shell: |
pct exec {{ mysql_container_id }} -- bash -c "mysql <<EOF
CREATE DATABASE IF NOT EXISTS wavelog;
CREATE USER IF NOT EXISTS 'wavelog'@'%' IDENTIFIED BY '{{ wavelog_password }}';
GRANT ALL PRIVILEGES ON wavelog.* TO 'wavelog'@'%';
FLUSH PRIVILEGES;
EOF"
when: db_creation.rc != 0
- name: Test Wavelog database connection
shell: |
pct exec {{ mysql_container_id }} -- mysql -u wavelog -p'{{ wavelog_password }}' -e "SELECT DATABASE();" wavelog
register: test_result
ignore_errors: yes
- name: Display result
debug:
msg: |
Database setup completed!
Host: 10.0.19.6
Port: 3306
Database: wavelog
Username: wavelog
Password: {{ wavelog_password }}
Test result: {{ test_result.stdout | default('Failed to connect') }}

View file

@ -1,13 +0,0 @@
# SSH Configuration for git.w5isp.com access
# This config allows users to SSH to git.w5isp.com for Git operations
# while still allowing direct SSH to caddy.w5isp.com
Host git.w5isp.com
HostName caddy.w5isp.com
Port 2222
User git
Host caddy.w5isp.com
HostName caddy.w5isp.com
Port 22
User graham

View file

@ -1,96 +0,0 @@
# Caddy reverse proxy configuration
# Managed by Ansible - do not edit manually
# Forgejo Git server
git.w5isp.com {
# Caddy will automatically obtain Let's Encrypt certificates
reverse_proxy http://100.109.233.14 {
# Tell backend that SSL is handled by proxy
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
}
}
# APRS application
aprs.me {
# Caddy will automatically obtain Let's Encrypt certificates
reverse_proxy http://100.93.178.105 {
# Tell backend that SSL is handled by proxy
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
# Enable WebSocket support
header_up Upgrade {http.request.header.Upgrade}
header_up Connection {http.request.header.Connection}
}
}
# Jellyfin media server
jellyfin.w5isp.com {
# Caddy will automatically obtain Let's Encrypt certificates
reverse_proxy http://10.0.16.1:8096 {
# Tell backend that SSL is handled by proxy
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
}
}
# Immich photo management
photos.w5isp.com {
# Caddy will automatically obtain Let's Encrypt certificates
reverse_proxy http://10.0.16.1:2283 {
# Tell backend that SSL is handled by proxy
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
# Immich requires these additional headers for proper operation
header_up X-Forwarded-For {remote}
}
}
# Ghost blog
w5isp.com {
# Caddy will automatically obtain Let's Encrypt certificates
reverse_proxy http://100.127.248.71 {
# Tell backend that SSL is handled by proxy
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
# Ghost requires these headers
header_up X-Forwarded-For {remote}
}
}
# Wavelog amateur radio logging
log.w5isp.com {
# Caddy will automatically obtain Let's Encrypt certificates
reverse_proxy http://100.112.57.20 {
# Tell backend that SSL is handled by proxy
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
# Wavelog requires these headers
header_up X-Forwarded-For {remote}
}
}
# Add more entries below as needed

View file

@ -1,22 +0,0 @@
---
- name: Check CephFS capacity
hosts: proxmox[0]
become: true
tasks:
- name: Check Ceph cluster capacity
command: ceph df
register: ceph_df
changed_when: false
- name: Display Ceph capacity
debug:
var: ceph_df.stdout_lines
- name: Get CephFS status
command: ceph fs status kubernetes-shared
register: cephfs_status
changed_when: false
- name: Display CephFS status
debug:
var: cephfs_status.stdout_lines

View file

@ -1,53 +0,0 @@
---
- name: Upgrade PostgreSQL to 16 and Install PostGIS
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Add PostgreSQL APT repository
command: pct exec {{ ct_id }} -- bash -c "wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | apt-key add - && echo 'deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
- name: Update apt cache
command: pct exec {{ ct_id }} -- apt-get update -qq
- name: Install PostgreSQL 16
command: pct exec {{ ct_id }} -- apt-get install -y postgresql-16 postgresql-client-16
- name: Check current PostgreSQL clusters
command: pct exec {{ ct_id }} -- pg_lsclusters
register: clusters
- name: Display current clusters
debug:
msg: "{{ clusters.stdout }}"
- name: Drop old PostgreSQL 16 cluster if exists
command: pct exec {{ ct_id }} -- pg_dropcluster 16 main --stop
ignore_errors: yes
- name: Upgrade cluster from 15 to 16
command: pct exec {{ ct_id }} -- pg_upgradecluster 15 main
- name: Install PostGIS for PostgreSQL 16
command: pct exec {{ ct_id }} -- apt-get install -y postgresql-16-postgis-3
- name: Enable PostGIS in APRS database
command: pct exec {{ ct_id }} -- sudo -u postgres psql -p 5433 -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
- name: Switch to port 5432 for PostgreSQL 16
command: pct exec {{ ct_id }} -- bash -c "sed -i 's/port = 5433/port = 5432/' /etc/postgresql/16/main/postgresql.conf && systemctl restart postgresql@16-main"
- name: Stop PostgreSQL 15
command: pct exec {{ ct_id }} -- systemctl stop postgresql@15-main
- name: Verify upgrade
command: pct exec {{ ct_id }} -- sudo -u postgres psql -c "SELECT version();"
register: version
- name: Display results
debug:
msg: |
PostgreSQL upgrade completed!
{{ version.stdout }}

View file

@ -1,71 +0,0 @@
---
- name: Upgrade PostgreSQL 15 to 16 in LXC
hosts: lab02
become: true
vars:
ct_id: 300
tasks:
- name: Stop PostgreSQL 15 service
command: pct exec {{ ct_id }} -- systemctl stop postgresql@15-main
- name: Add PostgreSQL APT repository key
command: pct exec {{ ct_id }} -- bash -c "wget -q -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -"
- name: Add PostgreSQL APT repository
command: pct exec {{ ct_id }} -- bash -c "echo 'deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
- name: Update apt cache
command: pct exec {{ ct_id }} -- apt-get update
- name: Install PostgreSQL 16
command: pct exec {{ ct_id }} -- apt-get install -y postgresql-16 postgresql-client-16 postgresql-contrib-16
- name: Stop PostgreSQL 16 (auto-started after install)
command: pct exec {{ ct_id }} -- systemctl stop postgresql@16-main
- name: Upgrade the cluster from 15 to 16
command: pct exec {{ ct_id }} -- sudo -u postgres /usr/lib/postgresql/16/bin/pg_upgrade -b /usr/lib/postgresql/15/bin -B /usr/lib/postgresql/16/bin -d /var/lib/postgresql/15/main -D /var/lib/postgresql/16/main -o "-c config_file=/etc/postgresql/15/main/postgresql.conf" -O "-c config_file=/etc/postgresql/16/main/postgresql.conf"
- name: Copy PostgreSQL 15 configuration
command: pct exec {{ ct_id }} -- bash -c "cp /etc/postgresql/15/main/postgresql.conf /etc/postgresql/16/main/ && cp /etc/postgresql/15/main/pg_hba.conf /etc/postgresql/16/main/"
- name: Update PostgreSQL 16 port to 5432
command: pct exec {{ ct_id }} -- sed -i "s/port = 5433/port = 5432/" /etc/postgresql/16/main/postgresql.conf
- name: Start PostgreSQL 16
command: pct exec {{ ct_id }} -- systemctl start postgresql@16-main
- name: Enable PostgreSQL 16
command: pct exec {{ ct_id }} -- systemctl enable postgresql@16-main
- name: Disable PostgreSQL 15
command: pct exec {{ ct_id }} -- systemctl disable postgresql@15-main
- name: Install PostGIS for PostgreSQL 16
command: pct exec {{ ct_id }} -- apt-get install -y postgresql-16-postgis-3
- name: Enable PostGIS in APRS database
command: pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "CREATE EXTENSION IF NOT EXISTS postgis;"
- name: Enable PostGIS in Forgejo database (if needed)
command: pct exec {{ ct_id }} -- sudo -u postgres psql -d forgejo -c "CREATE EXTENSION IF NOT EXISTS postgis;"
ignore_errors: yes
- name: Verify PostgreSQL version
command: pct exec {{ ct_id }} -- sudo -u postgres psql -c "SELECT version();"
register: pg_version
- name: Verify PostGIS installation
command: pct exec {{ ct_id }} -- sudo -u postgres psql -d aprs -c "SELECT PostGIS_Version();"
register: postgis_version
- name: Display results
debug:
msg: |
PostgreSQL upgrade completed!
{{ pg_version.stdout }}
PostGIS Version in APRS database:
{{ postgis_version.stdout }}

View file

@ -1,68 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
containers:
- name: wavelog
image: ghcr.io/wavelog/wavelog:latest
env:
- name: CI_ENV
value: "docker"
- name: WAVELOG_DB_HOST
value: "10.0.19.6"
- name: WAVELOG_DB_PORT
value: "3306"
- name: WAVELOG_DB_NAME
value: "wavelog"
- name: WAVELOG_DB_USER
value: "wavelog"
- name: WAVELOG_DB_PASS
valueFrom:
secretKeyRef:
name: wavelog-db-secret
key: password
ports:
- containerPort: 80
name: http
volumeMounts:
- name: config
mountPath: /var/www/html/application/config/docker
- name: uploads
mountPath: /var/www/html/uploads
- name: userdata
mountPath: /var/www/html/userdata
- name: backup
mountPath: /var/www/html/backup
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
imagePullSecrets:
- name: ghcr-pull-secret
volumes:
- name: config
persistentVolumeClaim:
claimName: wavelog-config
- name: uploads
persistentVolumeClaim:
claimName: wavelog-uploads
- name: userdata
persistentVolumeClaim:
claimName: wavelog-userdata
- name: backup
emptyDir: {}

View file

@ -1,89 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
containers:
- name: wavelog
image: php:8.2-apache
env:
- name: DATABASE_HOST
value: "10.0.19.6"
- name: DATABASE_PORT
value: "3306"
- name: DATABASE_NAME
value: "wavelog"
- name: DATABASE_USER
value: "wavelog"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: wavelog-db-secret
key: password
ports:
- containerPort: 80
name: http
volumeMounts:
- name: data
mountPath: /var/www/html
lifecycle:
postStart:
exec:
command:
- /bin/bash
- -c
- |
# Install dependencies if not already installed
if [ ! -f /var/www/html/.deps_installed ]; then
apt-get update && apt-get install -y \
git \
unzip \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libzip-dev \
libicu-dev \
mariadb-client \
curl \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd mysqli pdo pdo_mysql zip intl \
&& a2enmod rewrite \
&& touch /var/www/html/.deps_installed
fi
# Clone Wavelog if not exists
if [ ! -f /var/www/html/index.php ]; then
cd /tmp
git clone https://github.com/wavelog/wavelog.git
cp -R wavelog/* /var/www/html/
cp -R wavelog/.* /var/www/html/ 2>/dev/null || true
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
# Create uploads directory
mkdir -p /var/www/html/uploads
chown -R www-data:www-data /var/www/html/uploads
chmod -R 777 /var/www/html/uploads
fi
resources:
requests:
memory: "512Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: wavelog-data

View file

@ -1,133 +0,0 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wavelog-uploads
namespace: wavelog
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wavelog-userdata
namespace: wavelog
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wavelog-config
namespace: wavelog
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
containers:
- name: wavelog
image: ghcr.io/wavelog/wavelog:latest
env:
- name: CI_ENV
value: "docker"
- name: DB_HOST
value: "10.0.19.6"
- name: DB_NAME
value: "wavelog"
- name: DB_USER
value: "wavelog"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: wavelog-db-secret
key: password
ports:
- containerPort: 80
name: http
volumeMounts:
- name: config
mountPath: /var/www/html/application/config/docker
- name: uploads
mountPath: /var/www/html/uploads
- name: userdata
mountPath: /var/www/html/userdata
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
imagePullSecrets:
- name: ghcr-pull-secret
volumes:
- name: config
persistentVolumeClaim:
claimName: wavelog-config
- name: uploads
persistentVolumeClaim:
claimName: wavelog-uploads
- name: userdata
persistentVolumeClaim:
claimName: wavelog-userdata
---
apiVersion: v1
kind: Service
metadata:
name: wavelog
namespace: wavelog
spec:
selector:
app: wavelog
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http
---
apiVersion: v1
kind: Service
metadata:
name: wavelog-tailscale
namespace: wavelog
annotations:
tailscale.com/expose: "true"
tailscale.com/hostname: "wavelog"
tailscale.com/funnel: "false"
spec:
selector:
app: wavelog
type: LoadBalancer
loadBalancerClass: tailscale
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http

View file

@ -1,68 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
containers:
- name: wavelog
image: ghcr.io/wavelog/wavelog:latest
env:
- name: CI_ENV
value: "docker"
- name: WAVELOG_DB_HOST
value: "10.0.19.6"
- name: WAVELOG_DB_PORT
value: "3306"
- name: WAVELOG_DB_NAME
value: "wavelog"
- name: WAVELOG_DB_USER
value: "wavelog"
- name: WAVELOG_DB_PASS
valueFrom:
secretKeyRef:
name: wavelog-db-secret
key: password
ports:
- containerPort: 80
name: http
volumeMounts:
- name: config
mountPath: /var/www/html/application/config/docker
- name: uploads
mountPath: /var/www/html/uploads
- name: userdata
mountPath: /var/www/html/userdata
- name: backup
mountPath: /var/www/html/backup
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
imagePullSecrets:
- name: ghcr-pull-secret
volumes:
- name: config
persistentVolumeClaim:
claimName: wavelog-config
- name: uploads
persistentVolumeClaim:
claimName: wavelog-uploads
- name: userdata
persistentVolumeClaim:
claimName: wavelog-userdata
- name: backup
emptyDir: {}

View file

@ -1,108 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
initContainers:
- name: setup
image: php:8.2-apache
command:
- /bin/bash
- -c
- |
echo "Installing dependencies..."
apt-get update && apt-get install -y \
git unzip libpng-dev libjpeg-dev libfreetype6-dev \
libzip-dev libicu-dev mariadb-client curl \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd mysqli pdo pdo_mysql zip intl
echo "Cloning Wavelog..."
cd /tmp
git clone https://github.com/wavelog/wavelog.git
cp -R wavelog/* /var/www/html/
cp -R wavelog/.* /var/www/html/ 2>/dev/null || true
# Create config directory
mkdir -p /var/www/html/application/config/docker
# Create database.php config
cat > /var/www/html/application/config/database.php << 'EOF'
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => '',
'hostname' => '10.0.19.6',
'username' => 'wavelog',
'password' => 'm4icNmGTRtOMgrNxsxEdrw',
'database' => 'wavelog',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => FALSE,
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
EOF
# Set permissions
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
chmod -R 777 /var/www/html/uploads
chmod -R 777 /var/www/html/application/cache
chmod -R 777 /var/www/html/application/logs
chmod -R 777 /var/www/html/userdata
# Enable Apache rewrite module
a2enmod rewrite
echo "Setup complete!"
volumeMounts:
- name: data
mountPath: /var/www/html
containers:
- name: wavelog
image: php:8.2-apache
ports:
- containerPort: 80
name: http
volumeMounts:
- name: data
mountPath: /var/www/html
env:
- name: APACHE_DOCUMENT_ROOT
value: /var/www/html
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: wavelog-data

View file

@ -1,109 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavelog
namespace: wavelog
spec:
replicas: 1
selector:
matchLabels:
app: wavelog
template:
metadata:
labels:
app: wavelog
spec:
containers:
- name: wavelog
image: php:8.2-apache
env:
- name: CI_ENV
value: "production"
- name: DB_HOST
value: "10.0.19.6"
- name: DB_NAME
value: "wavelog"
- name: DB_USER
value: "wavelog"
- name: DB_PASS
valueFrom:
secretKeyRef:
name: wavelog-db-secret
key: password
ports:
- containerPort: 80
name: http
volumeMounts:
- name: data
mountPath: /var/www/html
command:
- /bin/bash
- -c
- |
# Install dependencies
apt-get update && apt-get install -y \
git \
unzip \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libzip-dev \
libicu-dev \
mariadb-client \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd mysqli pdo pdo_mysql zip intl \
&& a2enmod rewrite
# Clone Wavelog if not exists
if [ ! -d /var/www/html/index.php ]; then
cd /tmp
git clone https://github.com/wavelog/wavelog.git
cp -R wavelog/* /var/www/html/
chown -R www-data:www-data /var/www/html
# Create database config
cat > /var/www/html/application/config/database.php << EOF
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
\$active_group = 'default';
\$query_builder = TRUE;
\$db['default'] = array(
'dsn' => '',
'hostname' => '${DB_HOST}',
'username' => '${DB_USER}',
'password' => '${DB_PASS}',
'database' => '${DB_NAME}',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
EOF
fi
# Start Apache
apache2-foreground
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: wavelog-data

View file

@ -1,15 +0,0 @@
Wavelog Database Credentials
============================
MariaDB Container: 301 on lab02
Host: 10.0.19.6
Port: 3306
Database: wavelog
Username: wavelog
Password: m4icNmGTRtOMgrNxsxEdrw
Note: Database needs to be created manually in MariaDB container 301.
The root password for MariaDB may be required.
Connection String:
mysql://wavelog:m4icNmGTRtOMgrNxsxEdrw@10.0.19.6:3306/wavelog

View file

@ -1,233 +0,0 @@
#cloud-config
# Minimal cloud-init configuration for Debian 13 base template
# Update and upgrade packages on first boot
package_update: true
package_upgrade: true
# Configure timezone
timezone: America/Chicago
# Install essential packages
packages:
# System utilities
- sudo
- curl
- wget
- git
- vim
- nano
- tmux
- htop
- iotop
- rsync
- net-tools
- dnsutils
- traceroute
- mtr-tiny
- tcpdump
- nmap
- netcat-openbsd
# Development tools
- build-essential
- python3
- python3-pip
- python3-venv
- jq
- fzf
- zsh
# System monitoring
- sysstat
- smartmontools
- lm-sensors
# Time synchronization
- chrony
# Security
- fail2ban
- ufw
# Container runtime (optional)
- docker.io
- docker-compose
# Other useful tools
- apt-transport-https
- ca-certificates
- gnupg
- lsb-release
- software-properties-common
# Create users with sudo access
users:
- name: graham
groups: sudo,docker
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_import_id:
- gh:gmcintire
- name: andy
groups: sudo,docker
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_import_id:
- gh:nsnw
- name: ansible
uid: 10001
groups: sudo,docker
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5aTtW3lgF9PfV8sTIxZfbvo6dUBMq5C5zJrXy6mwdb5UK9xvqty5Vlh6F1FqYlZgvIyR7NqLbXnKqsvLVGdkTm9VEXGlNcbPJ3ilbhc3OJPX0k63CwokU6nSztXguE1v3KB7JjXiM0dj7ujuDzAGfPZNClR6u06NjIamzeWcQnsJV3P8rLxa7ppvFgBp0K1N1pMb3AUqXOutsOUjSvs5C4H+GMmKYCYNpPGbfKNDLfB9vYLPYv/ynF1lDDzBDL6gy0PnDX6lXC9jvfPWSKNu5lgIUc6p9J++I/ERGktbGPvEaLPeJjgIJPbVLwvnmCLDj2VBmvu2cVE+oXWjLJLF7y8T1cZA0d7aH1TWHcaFPNd3nsPbWGOA3PtMoNRLN/6b7mA7QAKCEBVcR1cZ8c5wQ7ilcLETrNBmJLPpBQa7tLvCx8Q8KPNmtZX6dMKR1o7yj5mXq7Slzo6RTdKLpDqODPVPmGboChMQWFEvGCKxVYLDuvS8iXHdkRE+C9xwqNis= ansible
# Configure SSH
ssh_pwauth: false
disable_root: true
# SSH configuration
write_files:
- path: /etc/ssh/sshd_config.d/99-custom.conf
content: |
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding yes
PrintMotd no
AcceptEnv LANG LC_*
Banner /etc/issue.net
- path: /etc/issue.net
content: |
******************************************************************
* *
* This system is for authorized users only. All activity is *
* monitored and reported. Unauthorized access is prohibited. *
* *
******************************************************************
- path: /etc/sudoers.d/90-cloud-init-users
content: |
# Created by cloud-init
graham ALL=(ALL) NOPASSWD:ALL
andy ALL=(ALL) NOPASSWD:ALL
ansible ALL=(ALL) NOPASSWD:ALL
permissions: '0440'
- path: /etc/motd
content: |
Welcome to Debian 13 (Trixie)
System Information:
* Documentation: https://www.debian.org/doc/
* Support: https://www.debian.org/support
- path: /etc/chrony/sources.d/custom.sources
content: |
pool 0.us.pool.ntp.org iburst
pool 1.us.pool.ntp.org iburst
pool 2.us.pool.ntp.org iburst
pool 3.us.pool.ntp.org iburst
- path: /etc/sysctl.d/99-custom.conf
content: |
# IP forwarding for containers/k8s
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
# Increase inotify limits for containers
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512
# Network performance tuning
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
# Security hardening
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
- path: /etc/security/limits.d/99-custom.conf
content: |
# Increase limits for containers
* soft nofile 65536
* hard nofile 65536
* soft nproc 32768
* hard nproc 32768
# Configure firewall (ufw)
ufw:
enabled: true
rules:
- port: 22
protocol: tcp
rule: allow
comment: SSH
- port: 80
protocol: tcp
rule: allow
comment: HTTP
- port: 443
protocol: tcp
rule: allow
comment: HTTPS
# Run commands on first boot
runcmd:
# Configure fail2ban
- systemctl enable fail2ban
- systemctl start fail2ban
# Enable and configure Docker
- systemctl enable docker
- systemctl start docker
- usermod -aG docker graham
- usermod -aG docker andy
- usermod -aG docker ansible
# Apply sysctl settings
- sysctl -p /etc/sysctl.d/99-custom.conf
# Configure chrony
- systemctl restart chrony
# Secure shared memory
- echo "tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0" >> /etc/fstab
- mount -o remount /run/shm
# Create common directories
- mkdir -p /opt/scripts /opt/configs
# Set proper permissions
- chmod 644 /etc/issue.net
- chmod 440 /etc/sudoers.d/90-cloud-init-users
# Update locate database
- updatedb
# Configure automatic updates
apt:
preserve_sources_list: true
conf: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
# Power state change timeout
power_state:
delay: now
mode: reboot
message: Initial configuration complete, rebooting...
timeout: 30
condition: true

View file

@ -1,225 +0,0 @@
#cloud-config
# Cloud-init configuration for Debian 13 Vultr server with Caddy proxy and Tailscale
# Update and upgrade packages on first boot
package_update: true
package_upgrade: true
# Configure hostname
hostname: caddy
# Configure timezone
timezone: America/Chicago
# Install packages
packages:
# Include all base packages
- sudo
- curl
- wget
- git
- vim
- tmux
- htop
- rsync
- net-tools
- dnsutils
- build-essential
- python3
- python3-pip
- apt-transport-https
- ca-certificates
- gnupg
- lsb-release
- software-properties-common
- fail2ban
- ufw
# Caddy and web server tools
- debian-keyring
- debian-archive-keyring
# Create users with sudo access
users:
- name: graham
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_import_id:
- gh:gmcintire
- name: andy
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_import_id:
- gh:nsnw
- name: ansible
uid: 10001
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5aTtW3lgF9PfV8sTIxZfbvo6dUBMq5C5zJrXy6mwdb5UK9xvqty5Vlh6F1FqYlZgvIyR7NqLbXnKqsvLVGdkTm9VEXGlNcbPJ3ilbhc3OJPX0k63CwokU6nSztXguE1v3KB7JjXiM0dj7ujuDzAGfPZNClR6u06NjIamzeWcQnsJV3P8rLxa7ppvFgBp0K1N1pMb3AUqXOutsOUjSvs5C4H+GMmKYCYNpPGbfKNDLfB9vYLPYv/ynF1lDDzBDL6gy0PnDX6lXC9jvfPWSKNu5lgIUc6p9J++I/ERGktbGPvEaLPeJjgIJPbVLwvnmCLDj2VBmvu2cVE+oXWjLJLF7y8T1cZA0d7aH1TWHcaFPNd3nsPbWGOA3PtMoNRLN/6b7mA7QAKCEBVcR1cZ8c5wQ7ilcLETrNBmJLPpBQa7tLvCx8Q8KPNmtZX6dMKR1o7yj5mXq7Slzo6RTdKLpDqODPVPmGboChMQWFEvGCKxVYLDuvS8iXHdkRE+C9xwqNis= ansible
# Configure SSH
ssh_pwauth: false
disable_root: true
# Write configuration files
write_files:
- path: /etc/ssh/sshd_config.d/99-custom.conf
content: |
PermitRootLogin no
PasswordAuthentication no
Banner /etc/issue.net
- path: /etc/issue.net
content: |
******************************************************************
* Authorized access only. All activity is monitored. *
******************************************************************
- path: /etc/apt/sources.list.d/caddy-stable.list
content: |
deb [signed-by=/usr/share/keyrings/caddy-stable-archive-keyring.gpg] https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version main
- path: /etc/caddy/Caddyfile
content: |
# Global options
{
# Email for Let's Encrypt
email admin@example.com
# Enable the admin endpoint
admin off
}
# Default site - return 404 for undefined hosts
:80 {
respond 404
}
# Example reverse proxy configuration
# example.com {
# reverse_proxy localhost:8080
# }
permissions: '0644'
owner: root:root
- path: /etc/sysctl.d/99-tailscale.conf
content: |
# Tailscale UDP GRO fix
net.ipv4.udp_rmem_min = 131072
net.ipv4.udp_wmem_min = 131072
net.core.rmem_default = 131072
net.core.wmem_default = 131072
net.core.netdev_max_backlog = 5000
- path: /opt/scripts/install-tailscale.sh
content: |
#!/bin/bash
# Install Tailscale
curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list
apt-get update
apt-get install -y tailscale
# Enable and start tailscaled
systemctl enable tailscaled
systemctl start tailscaled
# If TAILSCALE_KEY is provided, authenticate
if [ -n "$TAILSCALE_KEY" ]; then
tailscale up --authkey="$TAILSCALE_KEY" --accept-routes --hostname=$(hostname)
fi
permissions: '0755'
- path: /opt/scripts/setup-caddy.sh
content: |
#!/bin/bash
# Ensure Caddy starts after network is ready
mkdir -p /etc/systemd/system/caddy.service.d
cat > /etc/systemd/system/caddy.service.d/override.conf <<EOF
[Unit]
After=network-online.target
Wants=network-online.target
[Service]
# Restart on failure
Restart=on-failure
RestartSec=5s
EOF
systemctl daemon-reload
systemctl enable caddy
systemctl start caddy
permissions: '0755'
# Configure firewall
ufw:
enabled: true
rules:
- port: 22
protocol: tcp
rule: allow
comment: SSH
- port: 80
protocol: tcp
rule: allow
comment: HTTP
- port: 443
protocol: tcp
rule: allow
comment: HTTPS
- port: 41641
protocol: udp
rule: allow
from_any: true
comment: Tailscale
# Run commands on first boot
runcmd:
# Add Caddy GPG key and install
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
- apt-get update
- apt-get install -y caddy
# Apply sysctl settings
- sysctl -p /etc/sysctl.d/99-tailscale.conf
# Create Caddy directories
- mkdir -p /etc/caddy/sites-enabled
- chown -R caddy:caddy /etc/caddy
# Install Tailscale
- /opt/scripts/install-tailscale.sh
# Setup Caddy service
- /opt/scripts/setup-caddy.sh
# Configure fail2ban for Caddy
- |
cat > /etc/fail2ban/jail.d/caddy.conf <<EOF
[caddy-status]
enabled = true
filter = caddy-status
logpath = /var/log/caddy/access.log
maxretry = 5
bantime = 600
EOF
# Enable services
- systemctl enable fail2ban
- systemctl start fail2ban
# Final message
final_message: "Debian 13 Caddy proxy setup complete! System ready after $UPTIME seconds"
# Power state - don't reboot automatically for proxy servers
power_state:
delay: now
mode: poweroff
message: Initial configuration complete
condition: false

View file

@ -0,0 +1,74 @@
#!/bin/bash
# Script to properly join node3 to existing K3s etcd cluster
# Run this script ON node3
set -e
echo "=== Adding node3 to existing K3s etcd cluster ==="
# Step 1: Stop K3s and clean up
echo "Stopping K3s and cleaning up..."
sudo systemctl stop k3s || true
sudo systemctl stop k3s-agent || true
# Remove server data to prevent conflicts
echo "Cleaning up previous server data..."
sudo rm -rf /var/lib/rancher/k3s/server
# Step 2: Get the token from an existing server
echo ""
echo "You need to get the cluster token from node1 or node2."
echo "On node1 or node2, run:"
echo " sudo cat /var/lib/rancher/k3s/server/token"
echo ""
read -p "Enter the K3s cluster token: " K3S_TOKEN
# Step 3: Create proper K3s configuration for joining existing cluster
echo "Creating K3s configuration..."
sudo mkdir -p /etc/rancher/k3s
# Important: We need to specify this is joining an existing cluster
cat << EOF | sudo tee /etc/rancher/k3s/config.yaml
# Join existing cluster - do not initialize new one
server: "https://10.0.101.21:6443"
token: "${K3S_TOKEN}"
# Additional configuration
tls-san:
- "10.0.101.23"
- "node3"
node-ip: "10.0.101.23"
EOF
# Step 4: Uninstall K3s agent if it exists
if [ -f /usr/local/bin/k3s-agent-uninstall.sh ]; then
echo "Uninstalling K3s agent..."
sudo /usr/local/bin/k3s-agent-uninstall.sh
fi
# Step 5: Install K3s as server joining the existing cluster
echo "Installing K3s as server (joining existing cluster)..."
curl -sfL https://get.k3s.io | sh -s - server \
--server https://10.0.101.21:6443 \
--token "${K3S_TOKEN}"
# Step 6: Wait for services to start
echo "Waiting for K3s to start..."
sleep 20
# Step 7: Check status
echo "Checking K3s status..."
sudo systemctl status k3s --no-pager || true
sudo journalctl -u k3s -n 50 --no-pager
echo ""
echo "=== Setup complete ==="
echo "If you see errors about etcd already being initialized, that's expected."
echo "Check from your local machine with:"
echo " kubectl get nodes"
echo ""
echo "The node should now show roles: control-plane,etcd,master"
echo ""
echo "To check etcd cluster members from node1:"
echo " sudo k3s etcdctl member list"

View file

@ -0,0 +1,74 @@
---
apiVersion: v1
kind: Service
metadata:
name: postgres-aprs
namespace: aprs
spec:
selector:
app: postgres-aprs
ports:
- port: 5432
targetPort: 5432
clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres-aprs
namespace: aprs
spec:
serviceName: postgres-aprs
replicas: 1
selector:
matchLabels:
app: postgres-aprs
template:
metadata:
labels:
app: postgres-aprs
spec:
terminationGracePeriodSeconds: 600 # 10 minutes to shutdown gracefully
containers:
- name: postgres
image: postgis/postgis:17-3.4
ports:
- containerPort: 5432
envFrom:
- secretRef:
name: postgres-aprs-secret
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
livenessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
initialDelaySeconds: 300 # 5 minutes to start
periodSeconds: 30 # Check every 30s
timeoutSeconds: 10 # 10s timeout for probe
failureThreshold: 20 # Allow 20 failures (10 minutes)
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
initialDelaySeconds: 300 # 5 minutes to start
periodSeconds: 30 # Check every 30s
timeoutSeconds: 10 # 10s timeout for probe
failureThreshold: 20 # Allow 20 failures (10 minutes)
volumeClaimTemplates:
- metadata:
name: postgres-storage
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: longhorn
resources:
requests:
storage: 100Gi

View file

@ -0,0 +1,55 @@
---
apiVersion: v1
kind: Service
metadata:
name: postgres-aprs
namespace: aprs
spec:
selector:
app: postgres-aprs
ports:
- port: 5432
targetPort: 5432
clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres-aprs
namespace: aprs
spec:
serviceName: postgres-aprs
replicas: 1
selector:
matchLabels:
app: postgres-aprs
template:
metadata:
labels:
app: postgres-aprs
spec:
terminationGracePeriodSeconds: 3600 # 1 hour to shutdown gracefully
containers:
- name: postgres
image: postgis/postgis:17-3.4
ports:
- containerPort: 5432
envFrom:
- secretRef:
name: postgres-aprs-secret
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
# NO HEALTH CHECKS - Pod will run indefinitely even if unhealthy
volumeClaimTemplates:
- metadata:
name: postgres-storage
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: longhorn
resources:
requests:
storage: 100Gi

View file

@ -28,6 +28,7 @@ spec:
labels: labels:
app: postgres-aprs app: postgres-aprs
spec: spec:
terminationGracePeriodSeconds: 600 # 10 minutes to shutdown gracefully
containers: containers:
- name: postgres - name: postgres
image: postgis/postgis:17-3.4 image: postgis/postgis:17-3.4
@ -48,16 +49,20 @@ spec:
- /bin/sh - /bin/sh
- -c - -c
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB - pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
initialDelaySeconds: 30 initialDelaySeconds: 300 # 5 minutes to start
periodSeconds: 10 periodSeconds: 30 # Check every 30s
timeoutSeconds: 10 # 10s timeout for probe
failureThreshold: 20 # Allow 20 failures (10 minutes)
readinessProbe: readinessProbe:
exec: exec:
command: command:
- /bin/sh - /bin/sh
- -c - -c
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB - pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
initialDelaySeconds: 5 initialDelaySeconds: 300 # 5 minutes to start
periodSeconds: 5 periodSeconds: 30 # Check every 30s
timeoutSeconds: 10 # 10s timeout for probe
failureThreshold: 20 # Allow 20 failures (10 minutes)
volumeClaimTemplates: volumeClaimTemplates:
- metadata: - metadata:
name: postgres-storage name: postgres-storage

View file

@ -0,0 +1,10 @@
#!/bin/bash
# Restore normal mode for PostgreSQL (with health checks)
echo "Restoring PostgreSQL normal mode (with health checks)..."
export KUBECONFIG=/Users/graham/dev/infra/home/ansible/kubeconfig
kubectl apply -f postgres-deployment-normal.yaml
echo "PostgreSQL is now in normal mode with health checks enabled"
echo "Monitor with: kubectl logs -n aprs postgres-aprs-0 -f"

View file

@ -0,0 +1,11 @@
#!/bin/bash
# Enable repair mode for PostgreSQL (no health checks)
echo "Enabling PostgreSQL repair mode (no health checks)..."
export KUBECONFIG=/Users/graham/dev/infra/home/ansible/kubeconfig
kubectl apply -f postgres-deployment-repair.yaml
echo "PostgreSQL is now in repair mode - it will run indefinitely without health checks"
echo "Monitor with: kubectl logs -n aprs postgres-aprs-0 -f"
echo "To restore normal mode, run: ./postgres-normal-mode.sh"

View file

@ -0,0 +1,49 @@
#!/bin/bash
# Check K3s cluster HA readiness
echo "=== K3s Cluster HA Readiness Check ==="
echo ""
# Check nodes
echo "1. Node Status:"
kubectl get nodes -o wide
echo ""
# Check LoadBalancer services
echo "2. LoadBalancer Services (need to be accessible from any node):"
kubectl get svc -A | grep LoadBalancer
echo ""
# Check NodePort services
echo "3. NodePort Services (can be accessed on any node):"
kubectl get svc -A -o json | jq -r '.items[] | select(.spec.type=="NodePort") | "\(.metadata.namespace)/\(.metadata.name) - NodePorts: \(.spec.ports[].nodePort)"'
echo ""
# Check Traefik/Ingress
echo "4. Ingress Controller Status:"
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
kubectl get svc -n kube-system traefik
echo ""
# Check critical pods distribution
echo "5. Critical Pod Distribution:"
echo "Traefik pods:"
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik -o wide
echo -e "\nAPRS pods:"
kubectl get pods -n aprs -o wide
echo -e "\nAll pods on node1 (will be affected during update):"
kubectl get pods -A -o wide | grep node1
echo ""
# Recommendations
echo "=== Recommendations ==="
echo ""
echo "For MikroTik failover, ensure:"
echo "1. All services use NodePort or have hostPort configured"
echo "2. Configure MikroTik NAT to forward to NodePort on all nodes"
echo "3. Or use MikroTik Netwatch for automatic failover"
echo ""
echo "Current service ports:"
kubectl get svc -A | grep -E "NodePort|LoadBalancer" | awk '{print $2 " (" $1 "): " $6}'

View file

@ -0,0 +1,44 @@
# High Availability Setup for K3s Cluster
## Current Situation
- **node1** (10.0.101.21): Has public IP 204.110.191.2, currently being updated
- **node2** (10.0.101.22): No public IP
- **node3** (10.0.101.23): No public IP
## Solution Options
### Option 1: Floating IP (Recommended if your provider supports it)
Use a floating/virtual IP that can be moved between nodes:
1. Get a floating IP from your provider
2. Use Keepalived to manage automatic failover
3. Install MetalLB or Kube-VIP for in-cluster load balancing
### Option 2: External Load Balancer (Best for production)
Use your hosting provider's load balancer or set up HAProxy/Nginx:
1. Set up an external load balancer pointing to all 3 nodes
2. Configure health checks on port 6443 (K3s API)
3. For services, use NodePort and configure LB to forward to all nodes
### Option 3: DNS Round-Robin with Health Checks (Simple but less reliable)
Configure multiple A records with health checking:
1. Add A records for all node IPs to your domain
2. Use a DNS provider that supports health checks (like Route53)
3. Automatically remove unhealthy nodes from rotation
### Option 4: Tailscale + Caddy on Separate Host (Quick solution)
Since you have Tailscale, use a separate small VPS as a reverse proxy:
1. Get a small VPS with public IP
2. Install Tailscale and join your network
3. Install Caddy to reverse proxy to cluster services
4. Point DNS to this VPS instead of directly to cluster
## Immediate Solution: Configure Services for High Availability
While node1 is down, ensure your apps remain accessible:
### 1. Configure Pod Anti-Affinity
Ensure critical pods run on different nodes:

View file

@ -0,0 +1,177 @@
# High Availability Setup with MikroTik
## Overview
Using MikroTik's built-in features to ensure K3s cluster remains accessible when node1 is down.
## Option 1: MikroTik Load Balancing with PCC (Per Connection Classifier)
### On MikroTik Router:
```mikrotik
# 1. Create address list for K3s nodes
/ip firewall address-list
add address=10.0.101.21 list=k3s-nodes comment="node1"
add address=10.0.101.22 list=k3s-nodes comment="node2"
add address=10.0.101.23 list=k3s-nodes comment="node3"
# 2. Create NAT rules for incoming traffic to distribute to nodes
# For HTTP/HTTPS traffic (ports 80, 443)
/ip firewall nat
add chain=dstnat action=dst-nat protocol=tcp dst-address=204.110.191.2 \
dst-port=80 to-addresses=10.0.101.21-10.0.101.23 to-ports=80 \
comment="K3s HTTP load balance"
add chain=dstnat action=dst-nat protocol=tcp dst-address=204.110.191.2 \
dst-port=443 to-addresses=10.0.101.21-10.0.101.23 to-ports=443 \
comment="K3s HTTPS load balance"
# 3. Add connection marking for load balancing
/ip firewall mangle
add chain=prerouting action=mark-connection new-connection-mark=k3s-conn-1 \
dst-address=204.110.191.2 dst-port=80,443 protocol=tcp \
per-connection-classifier=both-addresses:3/0
add chain=prerouting action=mark-connection new-connection-mark=k3s-conn-2 \
dst-address=204.110.191.2 dst-port=80,443 protocol=tcp \
per-connection-classifier=both-addresses:3/1
add chain=prerouting action=mark-connection new-connection-mark=k3s-conn-3 \
dst-address=204.110.191.2 dst-port=80,443 protocol=tcp \
per-connection-classifier=both-addresses:3/2
```
## Option 2: MikroTik with VRRP (Virtual Router Redundancy Protocol)
### Prerequisites:
- Assign additional IPs to each node for VRRP
- Configure MikroTik to route to VRRP virtual IP
### On Each K3s Node:
```bash
# Install keepalived
sudo apt-get update && sudo apt-get install -y keepalived
# Create keepalived configuration
sudo tee /etc/keepalived/keepalived.conf << EOF
vrrp_instance VI_1 {
state BACKUP # All nodes start as BACKUP
interface eth0 # Your network interface
virtual_router_id 51 # Same on all nodes
priority $(hostname | grep -o '[0-9]*')0 # node1=10, node2=20, node3=30
advert_int 1
authentication {
auth_type PASS
auth_pass k3scluster
}
virtual_ipaddress {
10.0.101.100/24 # Virtual IP for cluster
}
}
EOF
# Start keepalived
sudo systemctl enable --now keepalived
```
### On MikroTik:
```mikrotik
# Route public IP traffic to VRRP virtual IP
/ip firewall nat
add chain=dstnat action=dst-nat protocol=tcp dst-address=204.110.191.2 \
dst-port=80,443 to-addresses=10.0.101.100 \
comment="K3s VRRP Virtual IP"
```
## Option 3: MikroTik Netwatch with Failover (Simplest)
This monitors nodes and updates NAT dynamically:
```mikrotik
# 1. Create scripts for failover
/system script
add name="k3s-use-node1" source={
/ip firewall nat set [find comment="K3s HTTP"] to-addresses=10.0.101.21
/ip firewall nat set [find comment="K3s HTTPS"] to-addresses=10.0.101.21
:log info "K3s traffic switched to node1"
}
add name="k3s-use-node2" source={
/ip firewall nat set [find comment="K3s HTTP"] to-addresses=10.0.101.22
/ip firewall nat set [find comment="K3s HTTPS"] to-addresses=10.0.101.22
:log info "K3s traffic switched to node2"
}
add name="k3s-use-node3" source={
/ip firewall nat set [find comment="K3s HTTP"] to-addresses=10.0.101.23
/ip firewall nat set [find comment="K3s HTTPS"] to-addresses=10.0.101.23
:log info "K3s traffic switched to node3"
}
# 2. Create initial NAT rules
/ip firewall nat
add chain=dstnat action=dst-nat protocol=tcp dst-address=204.110.191.2 \
dst-port=80 to-addresses=10.0.101.21 to-ports=80 comment="K3s HTTP"
add chain=dstnat action=dst-nat protocol=tcp dst-address=204.110.191.2 \
dst-port=443 to-addresses=10.0.101.21 to-ports=443 comment="K3s HTTPS"
# 3. Set up Netwatch monitors
/tool netwatch
add host=10.0.101.21 interval=10s timeout=5s \
down-script={
:if ([/tool netwatch get [find host=10.0.101.22] status]="up") do={
/system script run k3s-use-node2
} else={
:if ([/tool netwatch get [find host=10.0.101.23] status]="up") do={
/system script run k3s-use-node3
}
}
} \
up-script="/system script run k3s-use-node1"
add host=10.0.101.22 interval=10s timeout=5s
add host=10.0.101.23 interval=10s timeout=5s
```
## Recommended: Configure K3s Services for Any Node Access
Make sure your services use NodePort or LoadBalancer type:
```yaml
# Example service configuration
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 32080 # This port will be accessible on ALL nodes
selector:
app: myapp
```
Then configure MikroTik to forward to the NodePort on any available node.
## Testing the Failover
1. Verify current routing:
```bash
curl -v http://204.110.191.2
```
2. Shut down node1:
```bash
ssh node1 sudo shutdown -h now
```
3. Watch MikroTik logs and verify traffic switches:
```mikrotik
/log print follow where topics~"info"
```
4. Verify service remains accessible:
```bash
curl -v http://204.110.191.2
```

View file

@ -0,0 +1,63 @@
# K3s Cluster Update Instructions
## Update Order for Multi-Master Cluster
When updating a multi-master K3s cluster with embedded etcd, follow this order:
1. **Update node2 first** (secondary master)
2. **Update node1 second** (primary master)
3. **Update node3 last** (if it's successfully added to etcd cluster)
## Steps for Each Node
1. Copy the update script to the node:
```bash
scp update-k3s-node.sh node2:~/
```
2. SSH to the node and run the script:
```bash
ssh node2
chmod +x update-k3s-node.sh
./update-k3s-node.sh
```
3. When prompted about draining:
- Answer **'y'** for production safety (moves pods to other nodes)
- Answer **'n'** if you want faster update and can tolerate brief downtime
4. Monitor the update progress and verify the node comes back online
## Verification Commands
From your local machine:
```bash
# Check all nodes are ready and on correct version
kubectl get nodes -o wide
# Check etcd cluster health (run on any master node)
ssh node1 'sudo k3s etcdctl endpoint health'
# Check for any pods having issues
kubectl get pods -A | grep -v Running
# Check cluster info
kubectl cluster-info
```
## Rollback (if needed)
If something goes wrong, you can rollback to the previous version:
```bash
# On the affected node
sudo systemctl stop k3s
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.33.4+k3s1" INSTALL_K3S_SKIP_START=true sh -
sudo systemctl start k3s
```
## Important Notes
- The update process will cause a brief disruption to pods on that node
- Embedded etcd cluster requires majority of nodes to be online
- Always update secondary masters before primary master
- Monitor etcd logs during update: `sudo journalctl -u k3s -f | grep etcd`

64
home/cluster/update-k3s-node.sh Executable file
View file

@ -0,0 +1,64 @@
#!/bin/bash
# Script to update K3s on a node to version 1.33.5
# Run this script ON each node you want to update
set -e
NODE_NAME=$(hostname)
K3S_VERSION="v1.33.5+k3s1"
echo "=== Updating K3s on ${NODE_NAME} to ${K3S_VERSION} ==="
# Step 1: Check current version
echo "Current K3s version:"
k3s --version
# Step 2: Drain the node (make workloads move to other nodes)
echo ""
echo "Do you want to drain this node first? (recommended for safety)"
echo "This will move pods to other nodes during the update."
read -p "Drain node? (y/n): " DRAIN_NODE
if [[ "$DRAIN_NODE" == "y" ]]; then
echo "Draining node ${NODE_NAME}..."
kubectl drain ${NODE_NAME} --ignore-daemonsets --delete-emptydir-data --force --grace-period=60
fi
# Step 3: Stop K3s service
echo ""
echo "Stopping K3s service..."
sudo systemctl stop k3s
# Step 4: Download and install new K3s version
echo "Installing K3s ${K3S_VERSION}..."
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="${K3S_VERSION}" INSTALL_K3S_SKIP_START=true sh -
# Step 5: Start K3s with new version
echo "Starting K3s..."
sudo systemctl start k3s
# Step 6: Wait for node to be ready
echo "Waiting for K3s to be ready..."
sleep 20
# Step 7: Check new version
echo ""
echo "New K3s version:"
k3s --version
# Step 8: Check node status
kubectl get node ${NODE_NAME}
# Step 9: Uncordon the node if it was drained
if [[ "$DRAIN_NODE" == "y" ]]; then
echo ""
echo "Uncordoning node ${NODE_NAME}..."
kubectl uncordon ${NODE_NAME}
fi
echo ""
echo "=== Update complete on ${NODE_NAME} ==="
echo ""
echo "Verify cluster health with:"
echo " kubectl get nodes"
echo " kubectl get pods -A | grep -v Running"

35
terraform/.gitignore vendored
View file

@ -1,35 +0,0 @@
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc

View file

@ -1,97 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
### Terraform Workflow
```bash
# Initialize terraform providers
terraform init
# Validate configuration
terraform validate
# Plan changes
terraform plan
# Apply changes
terraform apply
# Show current state
terraform show
# Format terraform files
terraform fmt
```
### State Management
```bash
# List resources in state
terraform state list
# Show specific resource
terraform state show <resource_name>
# Import existing resources
terraform import <resource_type>.<name> <id>
```
## Architecture
This Terraform codebase manages DNS infrastructure across multiple providers:
### DNS Providers
- **DNSimple**: Primary domain registrar and DNS hosting
- **Porkbun**: Alternative DNS provider
- **PowerDNS**: Manages reverse DNS zones (204.110.188-191.x ranges)
### File Organization
- `main.tf`: Provider configurations and requirements
- `vars.tf`: Variable definitions (⚠️ contains hardcoded credentials that should be moved to environment variables)
- `dns_*.tf`: DNS zone and record configurations organized by domain/provider
- State is managed locally (`terraform.tfstate`)
### Managed Domains
- w5isp.com
- manero.org
- mcintire.me
- vntx.net, vntx.org
- gridmap.org
- Reverse DNS zones for 204.110.188-191.x IP ranges
### Security Considerations
**Critical**: API keys and tokens are hardcoded in `vars.tf` defaults. When making changes:
1. Never commit new credentials to the repository
2. Use environment variables: `export TF_VAR_variable_name=value`
3. Or use `-var` flags: `terraform apply -var="dnsimple_token=xxx"`
### Common Tasks
#### Adding DNS Records
1. Identify the appropriate `dns_*.tf` file for the domain
2. Add the record using the appropriate resource type:
```hcl
resource "dnsimple_zone_record" "example" {
zone_name = "domain.com"
name = "subdomain"
value = "192.168.1.1"
type = "A"
ttl = 3600
}
```
#### Importing Existing DNS Records
```bash
# DNSimple records
terraform import dnsimple_zone_record.name {zone_id}_{record_id}
# PowerDNS records
terraform import powerdns_record.name zone.com.:record_name:type
```
### Development Notes
- No modules are used; resources are organized by domain/function
- No automated testing or CI/CD pipeline exists
- Direct commits to main branch are the current workflow
- Run `terraform fmt` before committing to ensure consistent formatting