{"id":14459869,"url":"https://github.com/x-real-ip/infrastructure","last_synced_at":"2026-02-28T18:37:27.050Z","repository":{"id":37694189,"uuid":"453084825","full_name":"x-real-ip/infrastructure","owner":"x-real-ip","description":"My Infrastructure as Code, like configuration files, scripts, Ansible playbooks, and information needed to establish the fundamental components of my infrastructure, such as Proxmox- and Kubernetes cluster","archived":false,"fork":false,"pushed_at":"2025-10-28T19:57:55.000Z","size":5071,"stargazers_count":5,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-28T21:33:48.230Z","etag":null,"topics":["ansible","infrastructure-as-code","k8s","kubernetes","shell-script"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/x-real-ip.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-01-28T13:50:18.000Z","updated_at":"2025-10-28T19:57:59.000Z","dependencies_parsed_at":"2023-12-20T10:53:22.351Z","dependency_job_id":"fc647568-d82d-4c6e-b051-506198d90d68","html_url":"https://github.com/x-real-ip/infrastructure","commit_stats":null,"previous_names":["x-real-ip/infrastructure"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/x-real-ip/infrastructure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x-real-ip%2Finfrastructure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x-real-ip%2Finfrastructure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x-real-ip%2Finfrastructure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x-real-ip%2Finfrastructure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/x-real-ip","download_url":"https://codeload.github.com/x-real-ip/infrastructure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x-real-ip%2Finfrastructure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29946981,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T18:21:26.597Z","status":"ssl_error","status_checked_at":"2026-02-28T18:19:38.892Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","infrastructure-as-code","k8s","kubernetes","shell-script"],"created_at":"2024-09-01T21:00:34.393Z","updated_at":"2026-02-28T18:37:27.021Z","avatar_url":"https://github.com/x-real-ip.png","language":"Shell","funding_links":[],"categories":["ansible"],"sub_categories":[],"readme":"# infrastructure\n\n- [infrastructure](#infrastructure)\n  - [Ansible](#ansible)\n    - [Install Ansible](#install-ansible)\n    - [Ansible playbooks](#ansible-playbooks)\n  - [Kubernetes](#kubernetes)\n    - [Prerequisites](#prerequisites)\n    - [Add SSH keys on local device](#add-ssh-keys-on-local-device)\n    - [Setting up VM hosts on Proxmox](#setting-up-vm-hosts-on-proxmox)\n    - [Install k3s](#install-k3s)\n      - [Usage](#usage)\n    - [Local initialization and setup](#local-initialization-and-setup)\n      - [Kubectl](#kubectl)\n      - [Bitnami Kubeseal](#bitnami-kubeseal)\n    - [Kubernetes Cheatsheet](#kubernetes-cheatsheet)\n    - [Bitnami Sealed Secret](#bitnami-sealed-secret)\n    - [Node Feature Discovery](#node-feature-discovery)\n  - [Rsync](#rsync)\n  - [ISCSI](#iscsi)\n    - [Repair iSCSI share](#repair-iscsi-share)\n  - [TrueNAS](#truenas)\n    - [Rename volume](#rename-volume)\n  - [Resize VM disk](#resize-vm-disk)\n  - [Odroid](#odroid)\n- [Desktop](#desktop)\n  - [Debian](#debian)\n    - [OS installation](#os-installation)\n    - [Ansible installation](#ansible-installation)\n\n## Ansible\n\n### Install Ansible\n\nRemove old Debian package\n\n```bash\nsudo apt remove ansible -y\n```\n\nCreate a virtualenv for Ansible\n\n```bash\n\n```\n\n### Ansible playbooks\n\n| Playbook                             | Command                                                                         | Comment                                                   |\n| ------------------------------------ | ------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| non-root-user                        | ansible-playbook -k --ask-vault-password playbooks/non-root-user.yaml                     | Add a non root user                                       |\n| desktop                              | ansible-playbook -K --ask-vault-password playbooks/desktop.yaml                           | Set the Debian desktop desired state                      |\n| truenas-shares                       | ansible-playbook playbooks/truenas_shares.yaml                                            | Configure all NFS and ISCSI shares on the truenas hosts   |\n| truenas_switch-master                | ansible-playbook --ask-vault-password playbooks/truenas_switch-master.yaml                | Switch the master from A to B or the otherway around      |\n| shelly_update-firmware               | ansible-playbook playbooks/shelly_update-firmware.yaml                                    | Update and set desired state of all Shelly devices        |\n| k3s_rolling-update-nodes             | ansible-playbook --ask-vault-password playbooks/k3s_rolling-update-nodes.yaml             | Update the os packages on all k3s nodes                   |\n| k3s_install_cluster_minimal          | ansible-playbook --ask-vault-password playbooks/k3s_install_cluster_minimal.yaml          | Install or update k3s to the latest version all k3s nodes |\n| k3s_remove-apps-with-truenas-storage | ansible-playbook --ask-vault-password playbooks/k3s_remove-apps-with-truenas-storage.yaml | Delete all k8s resources that has storage=truenas label   |\n\n## Kubernetes\n\n### Prerequisites\n\n- Ansible installed on your local machine.\n- SSH access to the target machines where you want to install k3s.\n\n### Add SSH keys on local device\n\n1. Copy private and public key\n\n```bash\ncp /path/to/my/key/ansible ~/.ssh/ansible\ncp /path/to/my/key/ansible.pub ~/.ssh/ansible.pub\n```\n\n2. Change permissions on the files\n\n```bash\nsudo chmod 600 ~/.ssh/ansible\nsudo chmod 600 ~/.ssh/ansible.pub\n```\n\n3. Make ssh agent to actually use copied key\n\n```bash\nssh-add ~/.ssh/ansible\n```\n\n### Setting up VM hosts on Proxmox\n\n1. Create a VM with the following partitions\n\n   - / (10 GB) xfs\n   - /var (50GB) xfs\n   - /boot (1GB)\n\n   No swap partition is needed for kubernetes\n\n2. Login to the VM and set hostname.\n   ```console\n   hostnamectl set-hostname \u003chostname\u003e\n   ```\n3. Reboot.\n4. Set static ip for the VM in the DHCP server.\n\n5. Reboot.\n6. Set this hostname in the ansible inventory hosts.ini file.\n\n### Install k3s\n\n#### Usage\n\n1. Clone this repository to your local machine:\n\n```\ngit clone https://github.com/x-real-ip/infrastructure.git\n```\n\n2. Modify the inventory file inventory.ini to specify the target machines where you want to install k3s. Ensure you have appropriate SSH access and privileges.\n3. Run one of the following Ansible playbooks to initialize the k3s master and optional worker nodes:\n   1. Bare Installation:\n      Execute the k3s_install_cluster_bare.yaml playbook to install a clean cluster without additional deployments:\n      ```\n      sudo ansible-playbook --ask-vault-pass -kK k3s_install_cluster_bare.yaml\n      ```\n   2. Minimal Installation with Additional Deployments:\n      Execute the k3s_install_cluster_minimal.yaml playbook to install the bare minimum plus additional deployments:\n      ```\n      sudo ansible-playbook --ask-vault-pass -kK k3s_install_cluster_minimal.yaml\n      ```\n\n### Local initialization and setup\n\nFlush dns cache, needed when the same hostnames are used in previous machines (optional).\n\n```console\nresolvectl flush-caches\n```\n\n#### Kubectl\n\nInstall kubectl on your local machine. Read the [following page](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/) to know how to install kubectl on Linux.\n\n#### Bitnami Kubeseal\n\nInstall Kubeseal locally to use Bitnami sealed serets in k8s.\n\n```console\nsudo wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.19.1/kubeseal-0.19.1-linux-amd64.tar.gz\n\nsudo tar xzvf kubeseal-0.19.1-linux-amd64.tar.gz\n\nsudo install -m 755 kubeseal /usr/local/bin/kubeseal\n```\n\n### Kubernetes Cheatsheet\n\nDrain and terminate all pods gracefully on the node while marking the node as unschedulable\n\n```console\nkubectl drain --ignore-daemonsets --delete-emptydir-data \u003cnodename\u003e\n```\n\nMake the node unschedulable\n\n```console\nkubectl cordon \u003cnodename\u003e\n```\n\nMake the node schedulable\n\n```console\nkubectl uncordon \u003cnodename\u003e\n```\n\nConvert to BASE64\n\n```console\necho -n '\u003cvalue\u003e' | base64\n```\n\nDecode a secret with config file data\n\n```console\nkubectl get secret \u003csecret_name\u003e -o jsonpath='{.data}' -n \u003cnamespace\u003e\n```\n\nCreate secret from file\n\n```console\nkubectl create secret generic \u003csecret name\u003e --from-file=\u003csecret filelocation\u003e --dry-run=client  --output=yaml \u003e secrets.yaml\n```\n\nRestart Pod\n\n```console\nkubectl rollout restart deployment \u003cdeployment name\u003e -n \u003cnamespace\u003e\n```\n\nChange PV reclaim policy\n\n```console\nkubectl patch pv \u003cpv-name\u003e -p \"{\\\"spec\\\":{\\\"persistentVolumeReclaimPolicy\\\":\\\"Retain\\\"}}\"\n```\n\nShell into pod\n\n```console\nkubectl exec -it \u003cpod_name\u003e -- /bin/bash\n```\n\nCopy to or from pod\n\n```console\nkubectl cp \u003cnamespace\u003e/\u003cpod\u003e:/tmp/foo /tmp/bar\n```\n\nReuse PV in PVC\n\n1. Remove the claimRef in the PV this will set the PV status from `Released` to `Available`\n\n```console\nkubectl patch pv \u003cpv_name\u003e -p '{\"spec\":{\"claimRef\": null}}'\n```\n\n2. Add `volumeName` in PVC\n\n```yaml\n---\nkind: PersistentVolumeClaim\napiVersion: v1\nmetadata:\n  name: pvc-iscsi-home-assistant-data\n  namespace: home-automation\n  labels:\n    app: home-assistant\nspec:\n  storageClassName: freenas-iscsi-csi\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 5Gi\n  volumeName: pvc-iscsi-home-assistant-data\n```\n\n### Bitnami Sealed Secret\n\nRaw mode\n\n```\necho -n foo | kubeseal --cert \"./sealed-secret-tls-2.crt\" --raw --scope cluster-wide\n```\n\nCreate TLS (unencrypted) secret\n\n```\nkubectl create secret tls cloudflare-tls --key origin-ca.pk --cert origin-ca.crt --dry-run=client -o yaml \u003e cloudflare-tls.yaml\n```\n\nEncrypt secret with custom public certificate.\n\n```console\nkubeseal --cert \"./sealed-secret-tls-2.crt\" --format=yaml \u003c secret.yaml \u003e sealed-secret.yaml\n```\n\nAdd sealed secret to configfile secret\n\n```console\necho -n \u003cmypassword_value\u003e | kubectl create secret generic \u003csecretname\u003e --dry-run=client --from-file=\u003cpassword_key\u003e=/dev/stdin -o json | kubeseal --cert ./sealed-secret-tls-2.crt -o yaml \\\n-n democratic-csi --merge-into \u003csecret\u003e.yaml\n```\n\nRaw sealed secret\n\n`strict` scope (default):\n\n```console\necho -n foo | kubeseal --raw --from-file=/dev/stdin --namespace bar --name mysecret\nAgBChHUWLMx...\n```\n\n`namespace-wide` scope:\n\n```console\necho -n foo | kubeseal --cert ./sealed-secret-tls-2.crt --raw --from-file=/dev/stdin --namespace bar --scope namespace-wide\nAgAbbFNkM54...\n```\n\n`cluster-wide` scope:\n\n```console\necho -n foo | kubeseal --cert ./sealed-secret-tls-2.crt --raw --from-file=/dev/stdin --scope cluster-wide\nAgAjLKpIYV+...\n```\n\nInclude the `sealedsecrets.bitnami.com/namespace-wide` annotation in the `SealedSecret`\n\n```yaml\nmetadata:\n  annotations:\n    sealedsecrets.bitnami.com/namespace-wide: \"true\"\n```\n\nInclude the `sealedsecrets.bitnami.com/cluster-wide` annotation in the `SealedSecret`\n\n```yaml\nmetadata:\n  annotations:\n    sealedsecrets.bitnami.com/cluster-wide: \"true\"\n```\n\n[Github](https://github.com/bitnami-labs/sealed-secrets)\n\n[AWS Bitnami tutorial](https://aws.amazon.com/blogs/opensource/managing-secrets-deployment-in-kubernetes-using-sealed-secrets/)\n\n[Blogpost Tutorial](https://itsmetommy.com/2020/06/26/kubernetes-sealed-secrets/)\n\n### Node Feature Discovery\n\nShow node lables\n\n```console\nkubectl get no -o json | jq .items[].metadata.labels\n```\n\n## Rsync\n\nrsync exact copy\n\n```console\nsudo rsync -axHAWXS --numeric-ids --info=progress2 /mnt/sourcePart/ /mnt/destPart\n```\n\n## ISCSI\n\nDiscovering targets in iSCSI server\n\n```console\nsudo iscsiadm --mode discovery -t sendtargets --portal storage-server-lagg.lan.stamx.nl\n```\n\nMount disk\n\n```console\nsudo iscsiadm --mode node --targetname iqn.2005-10.org.freenas.ctl:\u003cdisk-name\u003e --portal storage-server-lagg.lan.stamx.nl --login\n```\n\nUnmount disk\n\n```console\nsudo iscsiadm --mode node --targetname iqn.2005-10.org.freenas.ctl:\u003cdisk-name\u003e --portal storage-server-lagg.lan.stamx.nl -u\n```\n\n### Repair iSCSI share\n\n1. Make sure that the container that uses the volume has stopped.\n2. SSH into one of the nodes in the cluster and start discovery\n\n```bash\nsudo iscsiadm -m discovery -t st -p truenas-master.lan.stamx.nl \u0026\u0026 \\\nread -p \"Enter the disk name: \" DISKNAME \u0026\u0026 \\\nexport DISKNAME\n```\n\n3. Login to target\n\n```bash\nsudo iscsiadm --mode node --targetname iqn.2005-10.org.freenas.ctl:${DISKNAME} --portal truenas-master.lan.stamx.nl --login \u0026\u0026 \\\nsleep 5 \u0026\u0026 \\\nlsblk \u0026\u0026 \\\nread -p \"Enter the device ('sda' for example): \" DEVICENAME \u0026\u0026 \\\nexport DEVICENAME\n```\n\n4. Create a local mount point \u0026 mount to replay logfile\n\n```bash\nsudo mkdir -vp /mnt/data-0 \u0026\u0026 sudo mount /dev/${DEVICENAME} /mnt/data-0/\n```\n\n5. Unmount the device\n\n```bash\nsudo umount /mnt/data-0/\n```\n\n6. Run check / ncheck\n\n```bash\nsudo xfs_repair -n /dev/${DEVICENAME}; sudo xfs_ncheck /dev/${DEVICENAME}\necho \"If filesystem corruption was corrected due to replay of the logfile, the xfs_ncheck should produce a list of nodes and pathnames, instead of the errorlog.\"\n```\n\n8. If needed run xfs repair\n\n```bash\nsudo xfs_repair /dev/${DEVICENAME}\n```\n\n9.  Logout from target\n\n```bash\nsudo iscsiadm --mode node --targetname iqn.2005-10.org.freenas.ctl:${DISKNAME} --portal storage-server-lagg.lan.stamx.nl --logout\necho \"Volumes are now ready to be mounted as PVCs.\"\n```\n\n## TrueNAS\n\n### Rename volume\n\n```\nzfs rename r01_1tb/k8s/{current zvol name} r01_1tb/k8s/{new zvol name}\n```\n\n## Resize VM disk\n\n```\nsudo dnf install cloud-utils-growpart\n\nsudo growpart /dev/sda 2\n\nsudo pvresize /dev/sda2\n\nsudo lvextend -l +100%FREE /dev/mapper/rl-root\n\nsudo xfs_growfs /\n\n```\n\n## Odroid\n\n1. Download the minimal .xz-compressed image file from https://fi.mirror.armbian.de/archive/odroidc4/archive/\n2. Write the .xz compressed image with a tool USBImager or balenaEtcher\n3. Insert the SD/MMC and boot\n4. Login via SSH user `root` default password `1234`\n5. Run Ansible playbook for Odroid\n\n# Desktop\n\n## Debian\n\n### OS installation\n\n1. Install debian with the netinstall iso image without any desktop environment.\n2. After installation, login and switch to root using su\n\n```bash\nsu\n```\n\n3. Update apt\n\n```bash\napt update\n```\n\n4. Install desktop and packages\n\n```bash\napt install \\\n  gnome-core \\\n  git \\\n  ssh\n```\n\n5. Reboot\n\n```bash\nshutdown -r now\n```\n\n6. Fix network\n   After the restart, login, open the terminal.\n\n```bash\nsu\n```\n\n```bash\nmv /etc/network/interfaces /etc/network/interfaces.bak\n```\n\nAfter the restart, the wired or wireless network should work.\n\nChange wifi powersave setting from 3 to 2 in `etc/NetworkManager/conf.d/default-wifi-powersave-on.conf` to fix wifi issue\n\n```\n[connection]\nwifi.powersave = 2\n```\n\n### Ansible installation\n\n1. Install pipx\n\n```bash\nsudo apt update\nsudo apt install pipx\npipx ensurepath\n```\n\n2. Install the minimal `ansible-core` package using pipx\n\n```bash\npipx install ansible-core\n```\n\n3. Install collections from the requirements.yaml located in the Ansible dir (optional)\n\n```bash\nansible-galaxy collection install -r requirements.yaml\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx-real-ip%2Finfrastructure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fx-real-ip%2Finfrastructure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx-real-ip%2Finfrastructure/lists"}