Add Gitea Actions runner, ansible-lint CI, vault setup, and fix group_vars layout
- Move group_vars into inventory/ so playbooks can find them (was only visible to ad-hoc commands via CWD) - Add Ansible Vault for sensitive variables (gitea/grafana passwords, tokens, db credentials) with vault_password_file outside the repo - Enable Gitea Actions and deploy act_runner with Docker backend on the gitea VM - Add .gitea/workflows/lint.yml to run ansible-lint on every push - Set up Gitea → GitHub push mirror (sync_on_commit) - Fix ansible.cfg stdout_callback for ansible-core 2.20 compatibility - Add python-psycopg2 to gitea role (required for postgresql modules) - Add docker to gitea_runner role (required by act_runner at startup) - Exclude password hashes and VM images from git Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(command ssh proxmox *)",
|
||||
"Bash(command ssh gitea-vm *)",
|
||||
"Bash(command ssh monitoring-vm *)",
|
||||
"Bash(scp -i /home/adyrem/dev/homelab/keys/homelab_ed25519 * root@192.168.1.10:*)",
|
||||
"Bash(scp -i /home/adyrem/dev/homelab/keys/homelab_ed25519 root@192.168.1.10:* *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Ansible Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ansible/**'
|
||||
- '.gitea/workflows/lint.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ansible/**'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: native
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install ansible-lint
|
||||
run: pip install ansible-lint --break-system-packages --quiet
|
||||
|
||||
- name: Run ansible-lint
|
||||
working-directory: ansible
|
||||
run: ansible-lint --profile=min playbooks/site.yml
|
||||
@@ -3,3 +3,6 @@ wireguard/
|
||||
*.vault
|
||||
*.secret
|
||||
ansible/vault_pass
|
||||
user_credentials.json
|
||||
archinstall-configs/creds.json
|
||||
vms/
|
||||
|
||||
+3
-1
@@ -4,8 +4,10 @@ remote_user = adyrem
|
||||
private_key_file = ../keys/homelab_ed25519
|
||||
roles_path = roles
|
||||
host_key_checking = False
|
||||
stdout_callback = yaml
|
||||
stdout_callback = default
|
||||
result_format = yaml
|
||||
diff_mode = True
|
||||
vault_password_file = ~/.config/homelab/vault_pass
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
31373161366133353539336439393963366530323131303662326630346565326664333330613031
|
||||
3064633138633062656230626261623132663839343162310a633964633136323634616230313334
|
||||
34363739636133363830393462376638326632383634313664333066366262323131636337613335
|
||||
6535353866363935310a363536633861326562626365653439386331643262646338633039316135
|
||||
64366432333635333162613862636534643730656266353839396165353337333934366633326134
|
||||
35313637386437306562616536383966336663663064656234306661626533323266336262363834
|
||||
37326130343966323764303562333038386231623737313762633437393266396365653132646133
|
||||
38633466363530666532336664636638306461643361613239373735343863646132313835393333
|
||||
62643763306265636362353934623030663966363530636535326632353637663164323137656239
|
||||
39336438626334353465613739323939646465393233363366653333363237393930353031393031
|
||||
33646633363337623664396162393630393365323630616532383233636233613238356236393337
|
||||
38646364616534303136333338396331333362303730633739353163646263396638623231323332
|
||||
64636564396633396436393535366366333238656234336261633433616538366434376565646236
|
||||
39373237663638666236306634643838613765653365396230363465386661623234626534393863
|
||||
61366336363839373939303138333865663031663338393938663265383564326335626263303563
|
||||
38646133646533363637653130616361646232613565346561316234383965353038373532633631
|
||||
64613432303761303161333162316465333432356266316533616636353561353534623032663534
|
||||
31316234346463316236623265356163383562643161626237393333316161663662336264333465
|
||||
32363236313862376436663537383531343562353462346132333062666261346462613839636462
|
||||
38323333356335653637323435643966386332333639623364373764306564646266343339653765
|
||||
64313936346663646131643135613161616261383233616237323663363063373632373936343831
|
||||
39663034623165623466653730323565653030616239356463303236653033353438323232666635
|
||||
33363237613263616264663861623365636230363934383861636266343064326136633430386634
|
||||
30373132623861386333343637633239353431383761656431643234356437656435653136343138
|
||||
35343635303931363464383665326134333634663033383937316135616263353763373036643932
|
||||
34376135623631313061306432356136623137373534643031326465666262393238333335666534
|
||||
33646532303633356163616465373366623866623930653939373336386331323435343635616563
|
||||
66356432636364373436646666613764313535363836666164333663323932623236313739306462
|
||||
34613439393834656535623437643431373761633865333234633866646531316131
|
||||
@@ -5,3 +5,4 @@
|
||||
- node_exporter
|
||||
- promtail
|
||||
- gitea
|
||||
- gitea_runner
|
||||
|
||||
@@ -3,10 +3,10 @@ gitea_http_port: 3000
|
||||
gitea_domain: "{{ ansible_host }}"
|
||||
gitea_db_name: gitea
|
||||
gitea_db_user: gitea
|
||||
gitea_db_password: gitea
|
||||
gitea_secret_key: "REDACTED_SECRET_KEY"
|
||||
gitea_internal_token: "REDACTED_INTERNAL_TOKEN"
|
||||
gitea_db_password: "{{ vault_gitea_db_password }}"
|
||||
gitea_secret_key: "{{ vault_gitea_secret_key }}"
|
||||
gitea_internal_token: "{{ vault_gitea_internal_token }}"
|
||||
|
||||
gitea_admin_user: adyrem
|
||||
gitea_admin_password: "" # set in vault or at runtime
|
||||
gitea_admin_email: "" # set in group_vars or vault
|
||||
gitea_admin_password: "{{ vault_gitea_admin_password }}"
|
||||
gitea_admin_email: "{{ vault_gitea_admin_email }}"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
name:
|
||||
- gitea
|
||||
- postgresql
|
||||
- python-psycopg2
|
||||
state: present
|
||||
|
||||
- name: Initialize PostgreSQL cluster
|
||||
|
||||
@@ -45,6 +45,9 @@ PROVIDER = db
|
||||
[cache]
|
||||
ADAPTER = memory
|
||||
|
||||
[actions]
|
||||
ENABLED = true
|
||||
|
||||
[indexer]
|
||||
ISSUE_INDEXER_TYPE = bleve
|
||||
REPO_INDEXER_ENABLED = true
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
gitea_runner_version: "0.2.11"
|
||||
gitea_runner_url: "http://10.10.1.125:3000"
|
||||
gitea_runner_name: "{{ inventory_hostname }}"
|
||||
gitea_runner_labels: "native:host"
|
||||
gitea_runner_registration_token: "{{ vault_gitea_runner_token }}"
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: Restart act_runner
|
||||
ansible.builtin.systemd:
|
||||
name: act_runner
|
||||
state: restarted
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
- name: Install act_runner dependencies
|
||||
community.general.pacman:
|
||||
name:
|
||||
- git
|
||||
- docker
|
||||
state: present
|
||||
|
||||
- name: Enable and start Docker
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Download act_runner binary
|
||||
ansible.builtin.get_url:
|
||||
url: "https://dl.gitea.com/act_runner/{{ gitea_runner_version }}/act_runner-{{ gitea_runner_version }}-linux-amd64"
|
||||
dest: /usr/local/bin/act_runner
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create act_runner user
|
||||
ansible.builtin.user:
|
||||
name: act_runner
|
||||
system: true
|
||||
shell: /sbin/nologin
|
||||
home: /var/lib/act_runner
|
||||
create_home: true
|
||||
groups: docker
|
||||
append: true
|
||||
|
||||
- name: Create act_runner config directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/act_runner
|
||||
state: directory
|
||||
owner: act_runner
|
||||
group: act_runner
|
||||
mode: "0750"
|
||||
|
||||
- name: Generate default act_runner config
|
||||
ansible.builtin.command:
|
||||
cmd: act_runner generate-config
|
||||
stdin: ""
|
||||
register: runner_config_out
|
||||
changed_when: false
|
||||
|
||||
- name: Write act_runner config
|
||||
ansible.builtin.copy:
|
||||
content: "{{ runner_config_out.stdout }}"
|
||||
dest: /etc/act_runner/config.yml
|
||||
owner: act_runner
|
||||
group: act_runner
|
||||
mode: "0640"
|
||||
force: false
|
||||
|
||||
- name: Set runner labels in config
|
||||
ansible.builtin.replace:
|
||||
path: /etc/act_runner/config.yml
|
||||
regexp: '^ labels:$'
|
||||
replace: " labels:\n - \"native:host\""
|
||||
notify: Restart act_runner
|
||||
|
||||
- name: Register runner with Gitea
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
act_runner register
|
||||
--no-interactive
|
||||
--instance "{{ gitea_runner_url }}"
|
||||
--token "{{ gitea_runner_registration_token }}"
|
||||
--name "{{ gitea_runner_name }}"
|
||||
--labels "{{ gitea_runner_labels }}"
|
||||
--config /etc/act_runner/config.yml
|
||||
chdir: /var/lib/act_runner
|
||||
creates: /var/lib/act_runner/.runner
|
||||
become_user: act_runner
|
||||
|
||||
- name: Install act_runner systemd service
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/act_runner.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Gitea Actions Runner
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=act_runner
|
||||
WorkingDirectory=/var/lib/act_runner
|
||||
ExecStart=/usr/local/bin/act_runner daemon --config /etc/act_runner/config.yml
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart act_runner
|
||||
|
||||
- name: Enable and start act_runner
|
||||
ansible.builtin.systemd:
|
||||
name: act_runner
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"app_config": null,
|
||||
"archinstall-language": "English",
|
||||
"auth_config": {},
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"removable": true,
|
||||
"uki": false
|
||||
},
|
||||
"custom_commands": [
|
||||
"mkdir -p /home/adyrem/.ssh && chmod 700 /home/adyrem/.ssh && echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdW4rhQFv561bYs8w0/VgR6AFgOLvVsAh6pcjZ/CO2R homelab' > /home/adyrem/.ssh/authorized_keys && chmod 600 /home/adyrem/.ssh/authorized_keys && chown -R adyrem:adyrem /home/adyrem/.ssh",
|
||||
"mkdir -p /vms/infra /vms/dev"
|
||||
],
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": null
|
||||
},
|
||||
"config_type": "manual_partitioning",
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "__SSD_DEVICE__",
|
||||
"wipe": true,
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": ["boot", "esp"],
|
||||
"fs_type": "fat32",
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "part-esp",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "GiB",
|
||||
"value": 1
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [
|
||||
{ "name": "@", "mountpoint": "/" },
|
||||
{ "name": "@home", "mountpoint": "/home" },
|
||||
{ "name": "@var", "mountpoint": "/var" },
|
||||
{ "name": "@snapshots", "mountpoint": "/.snapshots" },
|
||||
{ "name": "@vms-infra", "mountpoint": "/vms/infra" },
|
||||
{ "name": "@vms-dev", "mountpoint": "/vms/dev" }
|
||||
],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "btrfs",
|
||||
"mount_options": ["compress=zstd", "noatime"],
|
||||
"mountpoint": null,
|
||||
"obj_id": "part-root",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": "__ROOT_SIZE_BYTES__"
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 1074790400
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "homelab",
|
||||
"kernels": ["linux"],
|
||||
"locale_config": {
|
||||
"kb_layout": "de_CH-latin1",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_repositories": [],
|
||||
"custom_servers": [],
|
||||
"mirror_regions": {
|
||||
"Worldwide": ["https://geo.mirror.pkgbuild.com/$repo/os/$arch"]
|
||||
},
|
||||
"optional_repositories": []
|
||||
},
|
||||
"network_config": {
|
||||
"type": "nm"
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"base-devel",
|
||||
"git",
|
||||
"openssh",
|
||||
"networkmanager",
|
||||
"vim",
|
||||
"btrbk",
|
||||
"nftables"
|
||||
],
|
||||
"parallel_downloads": 5,
|
||||
"script": "guided",
|
||||
"services": ["sshd", "NetworkManager", "nftables"],
|
||||
"swap": {
|
||||
"algorithm": "zstd",
|
||||
"enabled": false
|
||||
},
|
||||
"timezone": "Europe/Zurich",
|
||||
"version": "4.1",
|
||||
"users": [
|
||||
{
|
||||
"enc_password": "$y$j9T$pqMooSvLvxBg2YP7eCQlq/$Ae8.dvSYB817bQkj5w9S4wu9OtWIq.p/pbWY6A/ythD",
|
||||
"groups": ["wheel"],
|
||||
"sudo": true,
|
||||
"username": "adyrem"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"app_config": null,
|
||||
"archinstall-language": "English",
|
||||
"auth_config": {},
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"removable": true,
|
||||
"uki": false
|
||||
},
|
||||
"custom_commands": [
|
||||
"mkdir -p /home/adyrem/.ssh && chmod 700 /home/adyrem/.ssh && echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdW4rhQFv561bYs8w0/VgR6AFgOLvVsAh6pcjZ/CO2R homelab' > /home/adyrem/.ssh/authorized_keys && chmod 600 /home/adyrem/.ssh/authorized_keys && chown -R adyrem:adyrem /home/adyrem/.ssh"
|
||||
],
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": null
|
||||
},
|
||||
"config_type": "manual_partitioning",
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "/dev/vda",
|
||||
"wipe": true,
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": ["boot", "esp"],
|
||||
"fs_type": "fat32",
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "part-esp",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "GiB",
|
||||
"value": 1
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [
|
||||
{ "name": "@", "mountpoint": "/" },
|
||||
{ "name": "@home", "mountpoint": "/home" },
|
||||
{ "name": "@var", "mountpoint": "/var" },
|
||||
{ "name": "@snapshots", "mountpoint": "/.snapshots" }
|
||||
],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "btrfs",
|
||||
"mount_options": ["compress=zstd"],
|
||||
"mountpoint": null,
|
||||
"obj_id": "part-root",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 20398997504
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 1074790400
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "arch-base",
|
||||
"kernels": ["linux"],
|
||||
"locale_config": {
|
||||
"kb_layout": "de_CH-latin1",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_repositories": [],
|
||||
"custom_servers": [],
|
||||
"mirror_regions": {
|
||||
"Worldwide": ["https://geo.mirror.pkgbuild.com/$repo/os/$arch"]
|
||||
},
|
||||
"optional_repositories": []
|
||||
},
|
||||
"network_config": {
|
||||
"type": "nm"
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"base-devel",
|
||||
"git",
|
||||
"openssh",
|
||||
"networkmanager",
|
||||
"vim",
|
||||
"btrbk",
|
||||
"qemu-guest-agent"
|
||||
],
|
||||
"parallel_downloads": 5,
|
||||
"script": "guided",
|
||||
"services": ["sshd", "NetworkManager", "qemu-guest-agent"],
|
||||
"swap": {
|
||||
"algorithm": "zstd",
|
||||
"enabled": false
|
||||
},
|
||||
"timezone": "Europe/Zurich",
|
||||
"version": "4.1",
|
||||
"users": [
|
||||
{
|
||||
"enc_password": "$y$j9T$pqMooSvLvxBg2YP7eCQlq/$Ae8.dvSYB817bQkj5w9S4wu9OtWIq.p/pbWY6A/ythD",
|
||||
"groups": ["wheel"],
|
||||
"sudo": true,
|
||||
"username": "adyrem"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"archinstall-language": "English",
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"removable": true,
|
||||
"uki": false
|
||||
},
|
||||
"custom_commands": [
|
||||
"mkdir -p /home/adyrem/.ssh && chmod 700 /home/adyrem/.ssh && echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdW4rhQFv561bYs8w0/VgR6AFgOLvVsAh6pcjZ/CO2R homelab' > /home/adyrem/.ssh/authorized_keys && chmod 600 /home/adyrem/.ssh/authorized_keys && chown -R adyrem:adyrem /home/adyrem/.ssh"
|
||||
],
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": null
|
||||
},
|
||||
"config_type": "manual_partitioning",
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "/dev/vda",
|
||||
"wipe": true,
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": ["boot", "esp"],
|
||||
"fs_type": "fat32",
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "part-esp",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "GiB",
|
||||
"value": 1
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [
|
||||
{ "name": "@", "mountpoint": "/" },
|
||||
{ "name": "@home", "mountpoint": "/home" },
|
||||
{ "name": "@var", "mountpoint": "/var" },
|
||||
{ "name": "@snapshots", "mountpoint": "/.snapshots" }
|
||||
],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "btrfs",
|
||||
"mount_options": ["compress=zstd"],
|
||||
"mountpoint": null,
|
||||
"obj_id": "part-root",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 20398997504
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 1074790400
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "gitea",
|
||||
"kernels": ["linux"],
|
||||
"locale_config": {
|
||||
"kb_layout": "de_CH-latin1",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_repositories": [],
|
||||
"custom_servers": [],
|
||||
"mirror_regions": {
|
||||
"Worldwide": ["https://geo.mirror.pkgbuild.com/$repo/os/$arch"]
|
||||
},
|
||||
"optional_repositories": []
|
||||
},
|
||||
"network_config": {
|
||||
"type": "nm"
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"base-devel",
|
||||
"git",
|
||||
"openssh",
|
||||
"networkmanager",
|
||||
"vim",
|
||||
"qemu-guest-agent"
|
||||
],
|
||||
"parallel_downloads": 5,
|
||||
"script": "guided",
|
||||
"services": ["sshd", "NetworkManager", "qemu-guest-agent"],
|
||||
"swap": {
|
||||
"algorithm": "zstd",
|
||||
"enabled": false
|
||||
},
|
||||
"timezone": "Europe/Zurich",
|
||||
"version": "4.3"
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"archinstall-language": "English",
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"removable": true,
|
||||
"uki": false
|
||||
},
|
||||
"custom_commands": [
|
||||
"mkdir -p /home/adyrem/.ssh && chmod 700 /home/adyrem/.ssh && echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdW4rhQFv561bYs8w0/VgR6AFgOLvVsAh6pcjZ/CO2R homelab' > /home/adyrem/.ssh/authorized_keys && chmod 600 /home/adyrem/.ssh/authorized_keys && chown -R adyrem:adyrem /home/adyrem/.ssh"
|
||||
],
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": null
|
||||
},
|
||||
"config_type": "manual_partitioning",
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "/dev/vda",
|
||||
"wipe": true,
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": ["boot", "esp"],
|
||||
"fs_type": "fat32",
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "part-esp",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "GiB",
|
||||
"value": 1
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [
|
||||
{ "name": "@", "mountpoint": "/" },
|
||||
{ "name": "@home", "mountpoint": "/home" },
|
||||
{ "name": "@var", "mountpoint": "/var" },
|
||||
{ "name": "@snapshots", "mountpoint": "/.snapshots" }
|
||||
],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "btrfs",
|
||||
"mount_options": ["compress=zstd"],
|
||||
"mountpoint": null,
|
||||
"obj_id": "part-root",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 41873833984
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": 1074790400
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "monitoring",
|
||||
"kernels": ["linux"],
|
||||
"locale_config": {
|
||||
"kb_layout": "de_CH-latin1",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_repositories": [],
|
||||
"custom_servers": [],
|
||||
"mirror_regions": {
|
||||
"Worldwide": ["https://geo.mirror.pkgbuild.com/$repo/os/$arch"]
|
||||
},
|
||||
"optional_repositories": []
|
||||
},
|
||||
"network_config": {
|
||||
"type": "nm"
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"base-devel",
|
||||
"git",
|
||||
"openssh",
|
||||
"networkmanager",
|
||||
"vim",
|
||||
"qemu-guest-agent"
|
||||
],
|
||||
"parallel_downloads": 5,
|
||||
"script": "guided",
|
||||
"services": ["sshd", "NetworkManager", "qemu-guest-agent"],
|
||||
"swap": {
|
||||
"algorithm": "zstd",
|
||||
"enabled": false
|
||||
},
|
||||
"timezone": "Europe/Zurich",
|
||||
"version": "4.3"
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
# Homelab Setup Requirements
|
||||
|
||||
## Hardware Context
|
||||
- Host machine with Intel i7-8700, 32GB RAM
|
||||
- Two disks: one smaller SSD (primary), one larger WD Black HDD (secondary)
|
||||
- Complete fresh install, nothing to preserve
|
||||
|
||||
---
|
||||
|
||||
## Non-Negotiable Requirements
|
||||
|
||||
1. Claude Code sessions can SSH into multiple VMs in the same session with full sudo access inside the VM.
|
||||
2. Claude Code sessions cannot execute any commands on the host — only read and write files in designated folders.
|
||||
3. A Windows VM with Visual Studio must be runnable and accessible via RDP.
|
||||
|
||||
---
|
||||
|
||||
## 1. Disk Layout
|
||||
|
||||
- Proxmox installed on the SSD using ZFS (single-disk pool)
|
||||
- ZFS chosen for native snapshots, send/receive, and compression
|
||||
- VM disk images stored in a ZFS pool on the SSD (`rpool` or a dedicated dataset)
|
||||
- HDD formatted as a ZFS pool, used for:
|
||||
- Receiving ZFS send/receive snapshots from SSD (local backup)
|
||||
- Storage for non-critical / parked VMs
|
||||
- Snapshot schedule: daily snapshots retained for 7 days, weekly retained for 4 weeks
|
||||
- Snapshot tooling: `sanoid` (policy-based ZFS snapshots) + `syncoid` (ZFS send/receive to HDD)
|
||||
|
||||
---
|
||||
|
||||
## 2. Host OS
|
||||
|
||||
- **Proxmox VE** (latest stable release, Debian-based)
|
||||
- Installed via the official Proxmox ISO — no custom OS required
|
||||
- No desktop environment on the host
|
||||
- Minimal additional packages — Proxmox manages the hypervisor; everything else runs in VMs
|
||||
- Locale: `en_US.UTF-8`, keyboard layout: Swiss German, timezone: `Europe/Zurich`
|
||||
- Hostname: `homelab`
|
||||
- Automatic security updates: `unattended-upgrades` configured for security patches only; full updates manual
|
||||
- Proxmox web UI accessible from local network / VPN only
|
||||
|
||||
---
|
||||
|
||||
## 3. Networking & Remote Access
|
||||
|
||||
### SSH
|
||||
- SSH access to host is **only permitted from within the local network or VPN** — Proxmox firewall blocks SSH from WAN
|
||||
- Key-based authentication only, password auth disabled
|
||||
- Root login disabled (admin user only)
|
||||
- A dedicated SSH keypair for this project; public key committed to repo, private key kept by operator
|
||||
- Dedicated non-default SSH port — to be decided during setup
|
||||
|
||||
### RDP
|
||||
- RDP is for VMs only — no RDP on the host
|
||||
- VM RDP accessible through VPN from outside the network
|
||||
|
||||
### Firewall
|
||||
- Proxmox built-in firewall enabled at datacenter and host level
|
||||
- Default deny inbound from WAN
|
||||
- Allow inbound from WAN: VPN port only
|
||||
- Allow inbound from local network / VPN: SSH (host), Proxmox web UI (8006), RDP (VMs), monitoring ports (Grafana, Prometheus)
|
||||
- VM-level firewall rules managed per VM in Proxmox
|
||||
|
||||
### VPN (Internet-Box)
|
||||
- Internet-Box 3 or 4 — use **IKEv2** (supported on both; WireGuard only on 4+, cannot confirm model)
|
||||
- VPN server configured on the Internet-Box itself (not on the host)
|
||||
- DynDNS configured on the Internet-Box for stable external hostname
|
||||
- All external access (SSH, RDP, Gitea, monitoring dashboards) routes through this VPN
|
||||
- Document VPN client setup steps for Windows and Linux clients in the repo README
|
||||
|
||||
---
|
||||
|
||||
## 4. Hypervisor
|
||||
|
||||
- **Proxmox VE** — KVM/QEMU managed via Proxmox (not libvirt)
|
||||
- VM management via Proxmox web UI and `qm` / `pvesh` CLI
|
||||
- VM networks (Proxmox Linux bridges):
|
||||
- `infra-net` (`vmbr1`): isolated NAT for infrastructure VMs (Gitea, monitoring)
|
||||
- `dev-net` (`vmbr2`): isolated NAT for Claude Code workload VMs
|
||||
- `desktop-net` (`vmbr3`): NAT for desktop VMs
|
||||
- No VM has bridged access to the physical LAN unless explicitly required
|
||||
- UEFI support via OVMF (included in Proxmox)
|
||||
- TPM 2.0 emulation via `swtpm` (required for Windows 11)
|
||||
- Infrastructure VMs (Gitea, monitoring) set to autostart on host boot via Proxmox autostart
|
||||
- VM configs (`.conf` files from `/etc/pve/qemu-server/`) committed to Ansible repo
|
||||
|
||||
---
|
||||
|
||||
## 5. Users & Permissions
|
||||
|
||||
### Host Users
|
||||
- One admin user (human operator):
|
||||
- SSH key login only, no password SSH
|
||||
- Full `sudo` access
|
||||
- One `claude-code` service user — enforces non-negotiable requirement 2:
|
||||
- **Shell set to `/usr/lib/openssh/sftp-server`** — SFTP-only, zero execution rights on host
|
||||
- **Chroot or path restriction** to designated read/write folders only (e.g. `/home/claude-code/workspace`)
|
||||
- SSH access restricted to a dedicated key
|
||||
- No access to Proxmox tooling, VM management, or other users' files
|
||||
- Cannot be used as a jump host — Claude Code SSHes directly into VMs
|
||||
|
||||
### VM Users (all VMs)
|
||||
- `root` account: password set, SSH login disabled
|
||||
- `vmuser` account: default non-root user, SSH key login, full sudo inside the VM
|
||||
- Additional users may be added per VM as needed
|
||||
|
||||
---
|
||||
|
||||
## 6. Virtual Machines
|
||||
|
||||
### 6.1 Gitea VM (Infrastructure)
|
||||
- **Distro:** Arch Linux minimal (provisioned via `archinstall` JSON config)
|
||||
- **Purpose:** Self-hosted Git, mirrors all repos to GitHub automatically
|
||||
- **Setup:** Provisioned manually as bootstrap step — must exist before Ansible automation can run
|
||||
- **Storage:** SSD ZFS pool (infrastructure-critical)
|
||||
- **Access:** SSH from host admin user; Gitea web UI accessible from local network / VPN
|
||||
- **Gitea config:**
|
||||
- Admin account set up manually
|
||||
- Mirror job configured for every repo to sync to GitHub
|
||||
- The Ansible homelab repo is the primary repo hosted here
|
||||
- **Backups:** Proxmox VM snapshot + ZFS send/receive to HDD; Gitea data directory snapshotted daily
|
||||
|
||||
### 6.2 Monitoring VM (Infrastructure)
|
||||
- **Distro:** Arch Linux minimal
|
||||
- **Purpose:** Centralised observability for host and all VMs
|
||||
- **Setup:** Provisioned via Ansible after Gitea bootstrap; autostart on host boot
|
||||
- **Storage:** SSD ZFS pool (infrastructure-critical)
|
||||
- **Access:** Grafana web UI accessible from local network / VPN only; no WAN exposure
|
||||
- **Backups:** Proxmox VM snapshot + ZFS send/receive to HDD
|
||||
|
||||
#### Stack
|
||||
| Component | Role |
|
||||
|---|---|
|
||||
| **Prometheus** | Metrics collection and storage |
|
||||
| **Grafana** | Dashboards and alerting UI |
|
||||
| **Node Exporter** | Host and VM system metrics (CPU, RAM, disk, network) |
|
||||
| **Alertmanager** | Alert routing (email or other notification channel) |
|
||||
| **Loki** | Log aggregation |
|
||||
| **Promtail** | Log shipping agent (runs on host and each VM) |
|
||||
|
||||
#### What Is Monitored
|
||||
- **Host system:** CPU, RAM, disk usage/IO, network throughput, ZFS pool health, Proxmox VM states
|
||||
- **All VMs:** CPU, RAM, disk, network via Node Exporter installed in each VM by Ansible
|
||||
- **Gitea VM:** Service health, disk usage of repo storage
|
||||
- **Windows VM:** Basic ping/availability check only (no agent)
|
||||
- **ZFS snapshots:** Alert if daily snapshot has not run within expected window
|
||||
- **VM availability:** Alert if any infrastructure VM (Gitea, monitoring itself) goes unreachable
|
||||
- **Disk space:** Alert at 75% and 90% on SSD and HDD ZFS pools
|
||||
|
||||
#### Alerting
|
||||
- Alertmanager configured with at least one notification channel (email recommended as default)
|
||||
- Alert on: host down, infrastructure VM down, disk >75%, snapshot missed, high CPU/RAM sustained >15 min
|
||||
|
||||
#### Ansible Integration
|
||||
- Node Exporter and Promtail installed and enabled on every Linux VM by the base VM playbook
|
||||
- Prometheus scrape config updated automatically when a new VM is provisioned
|
||||
- Monitoring VM definition and Prometheus/Grafana configs committed to Ansible repo
|
||||
- Grafana dashboards exported as JSON and committed to repo for full reproducibility
|
||||
|
||||
### 6.3 Windows VM
|
||||
- **Purpose:** Visual Studio development (extension building) — non-negotiable requirement 3
|
||||
- **License:** Activated via personal Microsoft account
|
||||
- **UEFI + TPM:** OVMF + swtpm for Windows 11 compatibility
|
||||
- **Resources:** 8 cores, 16GB RAM (adjustable)
|
||||
- **Storage:** SSD ZFS pool (active use); snapshot to HDD after each milestone
|
||||
- **Snapshot strategy:** ZFS snapshot of VM disk taken after:
|
||||
1. Clean Windows install + activation
|
||||
2. Visual Studio install + configuration
|
||||
- Snapshots sent to HDD ZFS pool; rebuild = `zfs receive` from HDD
|
||||
- **Access:** RDP via VPN from outside network
|
||||
- **Network:** `desktop-net`, no access to host or other VM networks
|
||||
- **Monitoring:** Ping/availability check only
|
||||
|
||||
### 6.4 Fedora KDE VM
|
||||
- **Purpose:** General purpose Linux desktop
|
||||
- **Distro:** Fedora (latest stable) with KDE Plasma desktop
|
||||
- **Resources:** 4 cores, 8GB RAM (adjustable)
|
||||
- **Storage:** HDD ZFS pool (non-critical)
|
||||
- **Users:** `root` + `vmuser`
|
||||
- **Access:** RDP via `xrdp` or VNC; SSH
|
||||
- **Network:** `desktop-net`
|
||||
- **Monitoring:** Node Exporter + Promtail installed via Ansible
|
||||
|
||||
### 6.5 Arch Desktop VM
|
||||
- **Purpose:** General purpose Linux desktop
|
||||
- **Distro:** Arch Linux with desktop environment (to be chosen at setup time)
|
||||
- **Resources:** 4 cores, 8GB RAM (adjustable)
|
||||
- **Storage:** HDD ZFS pool (non-critical)
|
||||
- **Users:** `root` + `vmuser`
|
||||
- **Access:** RDP or VNC; SSH
|
||||
- **Network:** `desktop-net`
|
||||
- **Monitoring:** Node Exporter + Promtail installed via Ansible
|
||||
|
||||
### 6.6 Claude Code Dev VMs
|
||||
- **Purpose:** Isolated environments for Claude Code to operate in — enforces non-negotiable requirement 1
|
||||
- **Distro:** Arch Linux minimal (provisioned via `archinstall` JSON config)
|
||||
- **Provisioning:** Cloned from a base template VM, then configured via Ansible
|
||||
- **Persistence:** VMs are persistent (not ephemeral per session)
|
||||
- **Multi-VM sessions:** Claude Code may SSH into multiple dev VMs in one session
|
||||
- **Isolation requirements:**
|
||||
- No shared folders with host
|
||||
- No access to `infra-net` or `desktop-net`
|
||||
- Operates on `dev-net` only
|
||||
- No route to host system
|
||||
- `vmuser` is the SSH target for Claude Code with full sudo inside the VM
|
||||
- **Resources:** 2 cores, 4GB RAM per VM (adjustable)
|
||||
- **Storage:** SSD ZFS pool for active VMs, HDD ZFS pool for parked/unused ones
|
||||
- **Monitoring:** Node Exporter + Promtail installed via Ansible base VM playbook
|
||||
|
||||
---
|
||||
|
||||
## 7. Ansible Automation
|
||||
|
||||
- All post-install configuration managed via Ansible playbooks
|
||||
- Repo hosted on Gitea VM, mirrored to GitHub
|
||||
- Secrets managed via `ansible-vault`; vault password stored only by the operator
|
||||
- **Playbook structure:**
|
||||
```
|
||||
homelab/
|
||||
├── archinstall-config.json # reproducible base config for Linux guest VMs
|
||||
├── inventory.ini # host + VM inventory
|
||||
├── playbooks/
|
||||
│ ├── host.yml # Proxmox host: users, SSH, firewall, sanoid/syncoid
|
||||
│ ├── snapshots.yml # ZFS snapshot schedules (sanoid) + send/receive (syncoid)
|
||||
│ ├── users.yml # host users, SSH keys, claude-code SFTP restriction
|
||||
│ ├── monitoring.yml # monitoring VM, Prometheus, Grafana, Loki
|
||||
│ └── vms/
|
||||
│ ├── gitea.yml
|
||||
│ ├── windows.yml
|
||||
│ ├── fedora-kde.yml
|
||||
│ ├── arch-desktop.yml
|
||||
│ └── dev-template.yml
|
||||
├── templates/ # Jinja2 config templates
|
||||
│ ├── prometheus.yml.j2
|
||||
│ ├── promtail.yml.j2
|
||||
│ ├── alertmanager.yml.j2
|
||||
│ └── sanoid.conf.j2
|
||||
├── files/
|
||||
│ └── grafana-dashboards/ # exported dashboard JSON files
|
||||
├── vars/
|
||||
│ └── vault.yml # encrypted secrets
|
||||
└── README.md
|
||||
```
|
||||
- Every playbook must be idempotent (safe to re-run)
|
||||
- README must document:
|
||||
- Full rebuild procedure from bare metal
|
||||
- VPN client setup (Windows + Linux)
|
||||
- How to provision a new dev VM
|
||||
- How to restore a VM from ZFS snapshot
|
||||
- How to access the Proxmox web UI and Grafana dashboard
|
||||
- How to add a new VM to monitoring
|
||||
|
||||
---
|
||||
|
||||
## 8. Backup & Recovery
|
||||
|
||||
- **Primary backup mechanism:** `syncoid` ZFS send/receive from SSD pool to HDD pool
|
||||
- **Snapshot policy:** managed by `sanoid` on both pools
|
||||
- **Scope of backups:**
|
||||
- All VM disks on SSD ZFS pool (daily incremental send/receive to HDD)
|
||||
- Gitea data directory (daily snapshot)
|
||||
- Monitoring VM data directory including Prometheus data (daily snapshot)
|
||||
- Grafana dashboards as JSON in git repo (always up to date)
|
||||
- Windows VM disk snapshot after each milestone (stored on HDD ZFS pool)
|
||||
- **No external/cloud backup initially** — architecture must make it easy to add (e.g. `restic` to Backblaze B2) later
|
||||
- **Recovery procedure documented in README**
|
||||
|
||||
---
|
||||
|
||||
## 9. Rebuild Order (Disaster Recovery Sequence)
|
||||
|
||||
In the event of full hardware loss, the rebuild order is:
|
||||
|
||||
1. Install Proxmox on new hardware (ISO installer, ~10 min)
|
||||
2. Restore SSD ZFS pool from HDD send/receive backups (`zfs receive`)
|
||||
3. Run `host.yml` Ansible playbook (cloned from GitHub mirror) to configure host users, SSH, firewall
|
||||
4. Start Gitea VM (restored from ZFS snapshot) and clone Ansible repo from it
|
||||
5. Run `monitoring.yml` to restore monitoring VM
|
||||
6. Run remaining playbooks to restore all VMs
|
||||
7. Import Grafana dashboards from JSON files in repo
|
||||
8. Reconfigure Internet-Box VPN and DynDNS if hardware changed
|
||||
|
||||
---
|
||||
|
||||
## 10. Out of Scope (for now)
|
||||
|
||||
- External/cloud backups (architecture must not preclude adding later)
|
||||
- Additional VM distros beyond those listed — template mechanism should make adding easy
|
||||
- Monitoring of Windows internals beyond availability ping
|
||||
Executable
+174
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run in the Arch live environment to generate archinstall-config-physical.json
|
||||
# with correct partition sizes for the target SSD.
|
||||
#
|
||||
# Usage:
|
||||
# ./generate-physical-config.sh /dev/sda # outputs to /tmp/config.json
|
||||
# ./generate-physical-config.sh /dev/nvme0n1p0 # NVMe example
|
||||
set -euo pipefail
|
||||
|
||||
info() { echo -e "\033[1;34m==>\033[0m $*"; }
|
||||
die() { echo -e "\033[1;31mERROR:\033[0m $*" >&2; exit 1; }
|
||||
|
||||
DEVICE="${1:-}"
|
||||
if [[ -z "$DEVICE" ]]; then
|
||||
echo "Usage: $0 <ssd-device>"
|
||||
echo
|
||||
echo "Available disks:"
|
||||
lsblk -d -o NAME,SIZE,MODEL
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -b "$DEVICE" ]] || die "Device '$DEVICE' not found or is not a block device."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Partition layout
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layout:
|
||||
# [1 MiB gap] [1 GiB ESP /boot] [rest: BTRFS root with subvolumes]
|
||||
#
|
||||
START_PADDING_B=1048576 # 1 MiB
|
||||
ESP_B=1073741824 # 1 GiB
|
||||
END_PADDING_B=1048576 # 1 MiB — leave 1 MiB at end for partition table alignment
|
||||
ROOT_START_B=$(( START_PADDING_B + ESP_B )) # = 1074790400
|
||||
|
||||
DISK_BYTES=$(lsblk -b -d -n -o SIZE "$DEVICE")
|
||||
ROOT_SIZE_B=$(( DISK_BYTES - ROOT_START_B - END_PADDING_B ))
|
||||
|
||||
info "Device: $DEVICE"
|
||||
info "Disk size: $DISK_BYTES bytes ($(( DISK_BYTES / 1024 / 1024 / 1024 )) GiB)"
|
||||
info "ESP start: $START_PADDING_B size: $ESP_B"
|
||||
info "Root start: $ROOT_START_B size: $ROOT_SIZE_B"
|
||||
|
||||
(( ROOT_SIZE_B > 0 )) || die "Disk is too small to partition."
|
||||
|
||||
OUT="/tmp/config.json"
|
||||
|
||||
cat > "$OUT" << EOF
|
||||
{
|
||||
"app_config": null,
|
||||
"archinstall-language": "English",
|
||||
"auth_config": {},
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"removable": true,
|
||||
"uki": false
|
||||
},
|
||||
"custom_commands": [
|
||||
"mkdir -p /home/adyrem/.ssh && chmod 700 /home/adyrem/.ssh && echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdW4rhQFv561bYs8w0/VgR6AFgOLvVsAh6pcjZ/CO2R homelab' > /home/adyrem/.ssh/authorized_keys && chmod 600 /home/adyrem/.ssh/authorized_keys && chown -R adyrem:adyrem /home/adyrem/.ssh",
|
||||
"mkdir -p /vms/infra /vms/dev"
|
||||
],
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": null
|
||||
},
|
||||
"config_type": "manual_partitioning",
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "$DEVICE",
|
||||
"wipe": true,
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": ["boot", "esp"],
|
||||
"fs_type": "fat32",
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "part-esp",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "GiB",
|
||||
"value": 1
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [
|
||||
{ "name": "@", "mountpoint": "/" },
|
||||
{ "name": "@home", "mountpoint": "/home" },
|
||||
{ "name": "@var", "mountpoint": "/var" },
|
||||
{ "name": "@snapshots", "mountpoint": "/.snapshots" },
|
||||
{ "name": "@vms-infra", "mountpoint": "/vms/infra" },
|
||||
{ "name": "@vms-dev", "mountpoint": "/vms/dev" }
|
||||
],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "btrfs",
|
||||
"mount_options": ["compress=zstd", "noatime"],
|
||||
"mountpoint": null,
|
||||
"obj_id": "part-root",
|
||||
"size": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": $ROOT_SIZE_B
|
||||
},
|
||||
"start": {
|
||||
"sector_size": { "unit": "B", "value": 512 },
|
||||
"unit": "B",
|
||||
"value": $ROOT_START_B
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "homelab",
|
||||
"kernels": ["linux"],
|
||||
"locale_config": {
|
||||
"kb_layout": "de_CH-latin1",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_repositories": [],
|
||||
"custom_servers": [],
|
||||
"mirror_regions": {
|
||||
"Worldwide": ["https://geo.mirror.pkgbuild.com/\$repo/os/\$arch"]
|
||||
},
|
||||
"optional_repositories": []
|
||||
},
|
||||
"network_config": {
|
||||
"type": "nm"
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"base-devel",
|
||||
"git",
|
||||
"openssh",
|
||||
"networkmanager",
|
||||
"vim",
|
||||
"btrbk",
|
||||
"nftables"
|
||||
],
|
||||
"parallel_downloads": 5,
|
||||
"script": "guided",
|
||||
"services": ["sshd", "NetworkManager", "nftables"],
|
||||
"swap": {
|
||||
"algorithm": "zstd",
|
||||
"enabled": false
|
||||
},
|
||||
"timezone": "Europe/Zurich",
|
||||
"version": "4.1",
|
||||
"users": [
|
||||
{
|
||||
"enc_password": "\$y\$j9T\$pqMooSvLvxBg2YP7eCQlq/\$Ae8.dvSYB817bQkj5w9S4wu9OtWIq.p/pbWY6A/ythD",
|
||||
"groups": ["wheel"],
|
||||
"sudo": true,
|
||||
"username": "adyrem"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo "Config written to $OUT"
|
||||
echo "Run: archinstall --config $OUT"
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run this inside the Arch live environment on physical hardware.
|
||||
# Fetches the config generator from the host HTTP server, generates the config
|
||||
# for the correct SSD device, then starts archinstall.
|
||||
#
|
||||
# Usage:
|
||||
# curl http://<host-ip>:8080/scripts/physical-install.sh | bash
|
||||
# or with device override:
|
||||
# curl http://<host-ip>:8080/scripts/physical-install.sh -o /tmp/install.sh
|
||||
# bash /tmp/install.sh /dev/sda
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${HOST:-http://192.168.1.1:8080}" # update to your host's LAN IP
|
||||
DEVICE="${1:-}"
|
||||
|
||||
echo "==> Available disks:"
|
||||
lsblk -d -o NAME,SIZE,MODEL
|
||||
echo
|
||||
|
||||
if [[ -z "$DEVICE" ]]; then
|
||||
read -rp "Enter SSD device for OS install (e.g. /dev/sda): " DEVICE
|
||||
fi
|
||||
|
||||
echo "==> Fetching config generator..."
|
||||
curl -fsSL "$HOST/scripts/generate-physical-config.sh" -o /tmp/generate-config.sh
|
||||
chmod +x /tmp/generate-config.sh
|
||||
|
||||
echo "==> Generating config for $DEVICE..."
|
||||
/tmp/generate-config.sh "$DEVICE"
|
||||
|
||||
echo "==> Starting archinstall..."
|
||||
archinstall --config /tmp/config.json
|
||||
Executable
+268
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# setup-arch-vm.sh
|
||||
# Creates a minimal Arch Linux VM on the local Fedora host using KVM/libvirt.
|
||||
# VM disk images are stored under homelab/vms/.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
VM_DIR="$PROJECT_DIR/vms"
|
||||
ISO_DIR="$VM_DIR/iso"
|
||||
|
||||
VM_NAME="arch-base"
|
||||
VM_DISK="$VM_DIR/${VM_NAME}.qcow2"
|
||||
VM_RAM=2048 # MiB
|
||||
VM_CPUS=2
|
||||
VM_DISK_SIZE=20 # GiB
|
||||
|
||||
ARCH_ISO_URL="https://geo.mirror.pkgbuild.com/iso/2026.04.01/archlinux-2026.04.01-x86_64.iso"
|
||||
ARCH_ISO_SHA256="f14bf46afbe782d28835aed99bfa2fe447903872cb9f4b21153196d6ed1d48ae"
|
||||
ARCH_ISO="$ISO_DIR/archlinux-2026.04.01-x86_64.iso"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
info() { echo -e "\033[1;34m==>\033[0m $*"; }
|
||||
ok() { echo -e "\033[1;32m ✓\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m !\033[0m $*"; }
|
||||
die() { echo -e "\033[1;31mERROR:\033[0m $*" >&2; exit 1; }
|
||||
|
||||
require_cmd() { command -v "$1" &>/dev/null || die "'$1' not found. Install it and re-run."; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pre-flight checks
|
||||
# ---------------------------------------------------------------------------
|
||||
info "Running pre-flight checks..."
|
||||
|
||||
require_cmd virsh
|
||||
require_cmd virt-install
|
||||
require_cmd qemu-img
|
||||
require_cmd sha256sum
|
||||
|
||||
# KVM modules — load if not already present
|
||||
if ! lsmod | grep -q '^kvm'; then
|
||||
info "KVM modules not loaded — attempting to load..."
|
||||
if grep -q "GenuineIntel" /proc/cpuinfo 2>/dev/null; then
|
||||
sudo modprobe kvm_intel || die "Failed to load kvm_intel. Enable virtualisation in BIOS/UEFI."
|
||||
else
|
||||
sudo modprobe kvm_amd || die "Failed to load kvm_amd. Enable virtualisation in BIOS/UEFI."
|
||||
fi
|
||||
fi
|
||||
ok "KVM modules loaded"
|
||||
|
||||
# br_netfilter — required for virtnetworkd to configure bridge netfilter rules;
|
||||
# without it /proc/sys/net/bridge/ doesn't exist and net-start fails with EPERM
|
||||
if ! lsmod | grep -q '^br_netfilter'; then
|
||||
info "Loading br_netfilter module..."
|
||||
sudo modprobe br_netfilter || die "Failed to load br_netfilter kernel module."
|
||||
fi
|
||||
# Persist across reboots
|
||||
if [[ ! -f /etc/modules-load.d/libvirt.conf ]]; then
|
||||
echo -e "kvm_intel\nbr_netfilter" | sudo tee /etc/modules-load.d/libvirt.conf >/dev/null
|
||||
fi
|
||||
ok "br_netfilter loaded"
|
||||
|
||||
# libvirt daemons — Fedora uses modular socket-activated daemons, not monolithic libvirtd
|
||||
LIBVIRT_SOCKETS=(virtqemud virtnetworkd virtnodedevd virtstoraged virtsecretd)
|
||||
info "Enabling libvirt modular daemons..."
|
||||
for svc in "${LIBVIRT_SOCKETS[@]}"; do
|
||||
if ! systemctl is-active --quiet "${svc}.socket"; then
|
||||
sudo systemctl enable --now "${svc}.socket"
|
||||
fi
|
||||
# Remove the idle timeout so daemons don't shut down mid-operation
|
||||
if [[ ! -f /etc/systemd/system/${svc}.service.d/notimeout.conf ]]; then
|
||||
sudo mkdir -p "/etc/systemd/system/${svc}.service.d"
|
||||
printf '[Service]\nEnvironment=%s_ARGS=\n' "${svc^^}" \
|
||||
| sudo tee "/etc/systemd/system/${svc}.service.d/notimeout.conf" >/dev/null
|
||||
fi
|
||||
if ! systemctl is-active --quiet "${svc}.service"; then
|
||||
sudo systemctl start "${svc}.service" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
sudo systemctl daemon-reload
|
||||
ok "libvirt modular daemons ready"
|
||||
|
||||
# Group membership — required for polkit to allow bridge/network operations
|
||||
if ! id -nG | grep -qw libvirt; then
|
||||
info "Adding $USER to libvirt and kvm groups..."
|
||||
sudo usermod -aG libvirt,kvm "$USER"
|
||||
echo
|
||||
echo " ┌─────────────────────────────────────────────────────────┐"
|
||||
echo " │ Group membership updated. You must start a new shell │"
|
||||
echo " │ for the change to take effect, then re-run the script. │"
|
||||
echo " │ │"
|
||||
echo " │ Run: newgrp libvirt │"
|
||||
echo " │ Then: ./scripts/setup-arch-vm.sh │"
|
||||
echo " └─────────────────────────────────────────────────────────┘"
|
||||
exit 0
|
||||
fi
|
||||
ok "User is in libvirt group"
|
||||
VIRSH="virsh --connect qemu:///system"
|
||||
VIRT_INSTALL="virt-install --connect qemu:///system"
|
||||
QEMU_IMG="qemu-img"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Default network
|
||||
# ---------------------------------------------------------------------------
|
||||
info "Checking libvirt default network..."
|
||||
if ! $VIRSH net-list --all | grep -q 'default'; then
|
||||
info "Default network not defined — creating it..."
|
||||
$VIRSH net-define /usr/share/libvirt/networks/default.xml \
|
||||
|| die "Could not define default network. Is libvirt-daemon-config-network installed?"
|
||||
fi
|
||||
if ! $VIRSH net-list | grep -q 'default'; then
|
||||
# Remove a stale virbr0 left over from a previous failed start
|
||||
if ip link show virbr0 &>/dev/null; then
|
||||
info "Removing stale virbr0 bridge..."
|
||||
sudo ip link set virbr0 down 2>/dev/null || true
|
||||
sudo ip link delete virbr0 2>/dev/null || true
|
||||
fi
|
||||
$VIRSH net-start default || true # ignore "already active" race
|
||||
ok "Default network started"
|
||||
fi
|
||||
$VIRSH net-autostart default &>/dev/null || true
|
||||
ok "Default network ready"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Directory setup
|
||||
# ---------------------------------------------------------------------------
|
||||
info "Creating VM storage directories..."
|
||||
mkdir -p "$VM_DIR" "$ISO_DIR"
|
||||
|
||||
# Grant the qemu user search (execute) permission on every directory component
|
||||
# leading to the VM storage — QEMU runs as uid qemu and can't traverse home dirs.
|
||||
if command -v setfacl &>/dev/null; then
|
||||
dir="$VM_DIR"
|
||||
while [[ "$dir" != "/" ]]; do
|
||||
setfacl -m u:qemu:x "$dir" 2>/dev/null || sudo setfacl -m u:qemu:x "$dir"
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
# Also allow qemu to read/write inside the vms directory itself
|
||||
setfacl -m u:qemu:rwx "$VM_DIR" 2>/dev/null || sudo setfacl -m u:qemu:rwx "$VM_DIR"
|
||||
setfacl -m u:qemu:rx "$ISO_DIR" 2>/dev/null || sudo setfacl -m u:qemu:rx "$ISO_DIR"
|
||||
else
|
||||
warn "setfacl not found — install 'acl' package if QEMU reports permission denied."
|
||||
fi
|
||||
ok "Directories ready: $VM_DIR"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ISO download
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ -f "$ARCH_ISO" ]]; then
|
||||
info "ISO already present — verifying checksum..."
|
||||
echo "$ARCH_ISO_SHA256 $ARCH_ISO" | sha256sum --check --quiet \
|
||||
&& ok "Checksum OK" \
|
||||
|| { warn "Checksum mismatch — re-downloading ISO..."; rm -f "$ARCH_ISO"; }
|
||||
fi
|
||||
|
||||
if [[ ! -f "$ARCH_ISO" ]]; then
|
||||
info "Downloading Arch Linux ISO (~1.1 GB)..."
|
||||
curl -L --progress-bar -o "$ARCH_ISO" "$ARCH_ISO_URL"
|
||||
info "Verifying checksum..."
|
||||
echo "$ARCH_ISO_SHA256 $ARCH_ISO" | sha256sum --check --quiet \
|
||||
|| die "ISO checksum verification failed. File may be corrupt."
|
||||
ok "ISO downloaded and verified"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# VM disk image
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ -f "$VM_DISK" ]]; then
|
||||
warn "Disk image already exists at $VM_DISK"
|
||||
read -rp " Delete and recreate? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || die "Aborting — existing disk left intact."
|
||||
rm -f "$VM_DISK"
|
||||
fi
|
||||
|
||||
info "Creating ${VM_DISK_SIZE}G qcow2 disk image..."
|
||||
$QEMU_IMG create -f qcow2 "$VM_DISK" "${VM_DISK_SIZE}G" -q
|
||||
ok "Disk image created: $VM_DISK"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# VM definition
|
||||
# ---------------------------------------------------------------------------
|
||||
if $VIRSH dominfo "$VM_NAME" &>/dev/null; then
|
||||
warn "VM '$VM_NAME' already defined in libvirt."
|
||||
read -rp " Undefine and recreate? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || die "Aborting — existing VM left intact."
|
||||
$VIRSH destroy "$VM_NAME" 2>/dev/null || true
|
||||
$VIRSH undefine "$VM_NAME" --remove-all-storage 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Verify virbr0 is actually up — virtnetworkd may have timed out and torn it down
|
||||
if ! ip link show virbr0 &>/dev/null; then
|
||||
info "virbr0 is gone — restarting default network..."
|
||||
sudo systemctl restart virtnetworkd.service
|
||||
sleep 2
|
||||
$VIRSH net-start default 2>/dev/null || true
|
||||
ip link show virbr0 &>/dev/null || die "virbr0 still missing after restart — check virtnetworkd logs."
|
||||
fi
|
||||
|
||||
OVMF_CODE="/usr/share/edk2/ovmf/OVMF_CODE.fd"
|
||||
OVMF_VARS="/usr/share/edk2/ovmf/OVMF_VARS.fd"
|
||||
|
||||
info "Defining VM '$VM_NAME'..."
|
||||
$VIRT_INSTALL \
|
||||
--name "$VM_NAME" \
|
||||
--ram "$VM_RAM" \
|
||||
--vcpus "$VM_CPUS" \
|
||||
--cpu host-passthrough \
|
||||
--os-variant archlinux \
|
||||
--disk "path=$VM_DISK,format=qcow2,bus=virtio,cache=writeback" \
|
||||
--disk "path=$ARCH_ISO,device=cdrom,readonly=on,bus=sata" \
|
||||
--network network=default,model=virtio \
|
||||
--graphics spice,listen=127.0.0.1 \
|
||||
--video qxl \
|
||||
--boot "loader=${OVMF_CODE},loader.readonly=yes,loader.type=pflash,nvram.template=${OVMF_VARS},cdrom,hd" \
|
||||
--noautoconsole
|
||||
|
||||
ok "VM '$VM_NAME' defined and started"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Get VM IP (may take a moment for DHCP)
|
||||
# ---------------------------------------------------------------------------
|
||||
info "Waiting for VM to get a DHCP address (up to 60s)..."
|
||||
VM_IP=""
|
||||
for i in $(seq 1 12); do
|
||||
sleep 5
|
||||
VM_IP=$($VIRSH domifaddr "$VM_NAME" 2>/dev/null | grep -oP '\d+\.\d+\.\d+\.\d+' | head -1 || true)
|
||||
[[ -n "$VM_IP" ]] && break
|
||||
echo -n "."
|
||||
done
|
||||
echo
|
||||
|
||||
if [[ -n "$VM_IP" ]]; then
|
||||
ok "VM IP address: $VM_IP"
|
||||
else
|
||||
warn "Could not detect VM IP yet — it may still be booting."
|
||||
warn "Run: virsh domifaddr $VM_NAME to check later."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " VM '$VM_NAME' is booting into the Arch Linux live environment."
|
||||
echo
|
||||
echo " Open the console in virt-manager, then run:"
|
||||
echo
|
||||
echo " 1. On your host — serve the config (if not already running):"
|
||||
echo " cd ~/dev/homelab && python3 -m http.server 8080"
|
||||
echo
|
||||
echo " 2. In the VM console — one-liner to fetch config and start install:"
|
||||
echo " curl http://192.168.122.1:8080/scripts/vm-install.sh | bash"
|
||||
echo
|
||||
echo " 3. After install completes, select Reboot."
|
||||
echo " SSH will be available on the installed system:"
|
||||
if [[ -n "$VM_IP" ]]; then
|
||||
echo " ssh -i ~/dev/homelab/keys/homelab_ed25519 adyrem@$VM_IP"
|
||||
fi
|
||||
echo
|
||||
echo " Arch ISO: $ARCH_ISO"
|
||||
echo " Disk image: $VM_DISK"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run this inside the Arch live environment to fetch the config and start archinstall.
|
||||
# Usage: curl http://192.168.122.1:8080/scripts/vm-install.sh | bash
|
||||
set -euo pipefail
|
||||
|
||||
HOST="http://192.168.122.1:8080"
|
||||
|
||||
echo "==> Fetching archinstall config..."
|
||||
curl -fsSL "$HOST/archinstall-config.json" -o /tmp/config.json
|
||||
|
||||
echo "==> Starting archinstall..."
|
||||
archinstall --config /tmp/config.json
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VM_NAME="arch-base"
|
||||
|
||||
virsh --connect qemu:///system change-media "$VM_NAME" sda --eject 2>/dev/null && echo "ISO ejected" || echo "No ISO to eject"
|
||||
virsh --connect qemu:///system reboot "$VM_NAME"
|
||||
echo "Rebooting $VM_NAME..."
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"app_config": null,
|
||||
"archinstall-language": "English",
|
||||
"auth_config": {},
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"removable": true,
|
||||
"uki": false
|
||||
},
|
||||
"custom_commands": [],
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": null
|
||||
},
|
||||
"config_type": "manual_partitioning",
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "/dev/vda",
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": [
|
||||
"boot",
|
||||
"esp"
|
||||
],
|
||||
"fs_type": "fat32",
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "3cbd14eb-4979-4273-a987-ca34d612c421",
|
||||
"size": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "GiB",
|
||||
"value": 1
|
||||
},
|
||||
"start": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [
|
||||
{
|
||||
"mountpoint": "/",
|
||||
"name": "@"
|
||||
},
|
||||
{
|
||||
"mountpoint": "/home",
|
||||
"name": "@home"
|
||||
},
|
||||
{
|
||||
"mountpoint": "/var",
|
||||
"name": "@var"
|
||||
},
|
||||
{
|
||||
"mountpoint": "/.snapshots",
|
||||
"name": "@snapshots"
|
||||
}
|
||||
],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "btrfs",
|
||||
"mount_options": [
|
||||
"compress=zstd"
|
||||
],
|
||||
"mountpoint": "/",
|
||||
"obj_id": "52dfb6b1-f569-4499-8dbf-0fde2e900819",
|
||||
"size": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "B",
|
||||
"value": 20398997504
|
||||
},
|
||||
"start": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "B",
|
||||
"value": 1074790400
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
],
|
||||
"wipe": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "arch-base",
|
||||
"kernels": [
|
||||
"linux"
|
||||
],
|
||||
"locale_config": {
|
||||
"kb_layout": "ch",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_repositories": [],
|
||||
"custom_servers": [],
|
||||
"mirror_regions": {
|
||||
"Worldwide": [
|
||||
"https://geo.mirror.pkgbuild.com/$repo/os/$arch"
|
||||
]
|
||||
},
|
||||
"optional_repositories": []
|
||||
},
|
||||
"network_config": {
|
||||
"type": "nm"
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"base-devel",
|
||||
"git",
|
||||
"openssh",
|
||||
"networkmanager",
|
||||
"vim",
|
||||
"btrbk",
|
||||
"qemu-guest-agent"
|
||||
],
|
||||
"parallel_downloads": 0,
|
||||
"script": "guided",
|
||||
"services": [
|
||||
"sshd",
|
||||
"NetworkManager",
|
||||
"qemu-guest-agent"
|
||||
],
|
||||
"swap": {
|
||||
"algorithm": "zstd",
|
||||
"enabled": false
|
||||
},
|
||||
"timezone": "Europe/Zurich",
|
||||
"version": "4.1"
|
||||
}
|
||||
Reference in New Issue
Block a user