aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKumar Damani <me@kumardamani.net>2023-08-11 16:28:54 +0000
committerKumar Damani <me@kumardamani.net>2023-08-11 16:28:54 +0000
commita8a40383081441ebd3757a501364b9ac45eb421e (patch)
treef48b7040615af1bda263faa4d2657636c77ba2a4
parent9bae899e227e4899a5ee629bf305a00a39044bc2 (diff)
parent1f321db8949b3aeb80afdd7a94e92b7743c3e275 (diff)
Merge branch 'work' into 'main'
Work See merge request kdam0/home-lab!1
-rw-r--r--ansible/README.md82
-rw-r--r--ansible/ansible-lint.yaml116
-rw-r--r--ansible/ansible.cfg7
-rw-r--r--ansible/archi.pngbin0 -> 65066 bytes
-rw-r--r--ansible/group_vars/all/vault51
-rw-r--r--ansible/group_vars/all/vault.example26
-rw-r--r--ansible/hosts29
-rw-r--r--ansible/playbooks/init.yaml98
-rw-r--r--ansible/roles/base/tasks/main.yaml12
-rw-r--r--ansible/roles/caddy_add/tasks/main.yaml64
-rw-r--r--ansible/roles/docker/tasks/main.yaml34
-rw-r--r--ansible/roles/med_dl/tasks/main.yaml5
-rw-r--r--ansible/roles/med_dl/tasks/setup.yaml38
-rw-r--r--ansible/roles/med_dl/templates/docker-compose.yaml.j267
-rw-r--r--ansible/roles/monitoring/tasks/main.yaml5
-rw-r--r--ansible/roles/monitoring/tasks/setup.yaml20
-rw-r--r--ansible/roles/monitoring/templates/prometheus.yml.j247
-rw-r--r--ansible/roles/nextcloud/tasks/main.yaml5
-rw-r--r--ansible/roles/nextcloud/tasks/setup.yaml40
-rw-r--r--ansible/roles/nextcloud/templates/docker-compose.yaml.j241
-rw-r--r--ansible/roles/plex/tasks/main.yaml5
-rw-r--r--ansible/roles/plex/tasks/setup.yaml55
-rw-r--r--ansible/roles/torrents/tasks/main.yaml5
-rw-r--r--ansible/roles/torrents/tasks/setup.yaml79
-rw-r--r--ansible/roles/torrents/templates/settings.json.j273
-rw-r--r--ansible/roles/vault/tasks/main.yaml5
-rw-r--r--ansible/roles/vault/tasks/setup.yaml12
-rw-r--r--ansible/roles/vault/templates/docker-compose.yaml.j228
-rw-r--r--terraform/README.md42
-rw-r--r--terraform/con.env.example3
-rw-r--r--terraform/main.tf309
-rw-r--r--terraform/terraform.tf12
-rw-r--r--terraform/variables.tf45
33 files changed, 1460 insertions, 0 deletions
diff --git a/ansible/README.md b/ansible/README.md
new file mode 100644
index 0000000..f693916
--- /dev/null
+++ b/ansible/README.md
@@ -0,0 +1,82 @@
+This part of the repo is responsible for provisioning our
+home-lab services using Ansible on top of the infrastructure already
+created via Terraform.
+
+The basic pattern is to confiure the service, and then add the upstream host
+to our reverse proxy (Caddy) configuration.
+
+![architecture image](archi.png)
+
+# TODOs (in-progress)
+1. reverse-proxy init
+1. minio user,bucket init
+1. static sites using minio bucket
+
+# Done
+1. Adding service to reverse-proxy (Caddy) via api
+1. Media Downloader with Transmission, and `*arr` services
+1. Plex
+1. Password Manager
+1. Nextcloud
+1. Metrics monitoring
+
+---
+
+# Pre-reqs
+* `pip`3 - `sudo apt install python3 python3-pip`
+* `ansible` - `pip install --user ansible`
+
+# Execution
+1. (First Time Only) Fork this repo, if you haven't already.
+1. (FTO) Make sure that your [`hosts`](./hosts) have been created in the [Terraform](../terraform) section.
+1. (FTO) Edit `group_vars/all/vault.example` to your liking.
+```bash
+echo "YourSecretPassword" > vaultkey
+ansible-vault encrypt --output group_vars/all/vault --encrypt-vault-id vaultkey group_vars/all/vault.example
+git checkout group_vars/all/vault.example
+```
+1. View/edit your encrypted vault:
+```bash
+ansible-vault view/edit --vault-id vaultkey group_vars/all/vault
+```
+1. (FTO) Run the `init.yaml` playbook to install a service
+```bash
+ansible-playbook -u youruser -i hosts --vault-id vaultkey playbooks/init.yaml --tags=[app name]
+```
+1. Use another playbook to modify a service (not yet implemented but planned)
+```bash
+ansible-playbook -u youruser -i hosts --vault-id vaultkey playbooks/... --tags=[...]
+```
+
+# Ansible Roles
+You can think of each Role being an "app".
+
+Each Role has a `setup.yaml` task file responsible for installing the app.
+It may or may not have other task files depending on what other functionality this Role
+supports for this app.
+
+The `main.yaml` file in the Role determines the flow logic for which task files
+are getting run based on Tags for other functionalities (see Tags section below).
+
+
+# Artifacts
+The directory `playbooks/artifacts/` is a place where we save the output for some of the roles.
+
+This not only serves the purpose to tracking important state info for our configurations,
+but it also allows us to use some of this info in other parts of Ansible for cleaner automation.
+
+> Never commit this dir to git. It contains sensitive info! Its already configured to
+be ignored by git.
+
+
+# Ansible Tags
+Setting up the app is normally done using the Role's name as the tag,
+i.e. `--tags <role name>`.
+
+Any subsequent tasks required beyond setting up the app, will have a different
+tag associated for those tasks (along with a different playbook to trigger them).
+For example, the MinIO Role will support adding a new user+bucket under the tag `add_minio_user`:
+
+```bash
+ansible-playbook ... playbooks/add_user.yaml --tags add_minio_user
+```
diff --git a/ansible/ansible-lint.yaml b/ansible/ansible-lint.yaml
new file mode 100644
index 0000000..ce3d0bd
--- /dev/null
+++ b/ansible/ansible-lint.yaml
@@ -0,0 +1,116 @@
+---
+profile: null # min, basic, moderate,safety, shared, production
+
+# Allows dumping of results in SARIF format
+# sarif_file: result.sarif
+
+# exclude_paths included in this file are parsed relative to this file's location
+# and not relative to the CWD of execution. CLI arguments passed to the --exclude
+# option are parsed relative to the CWD of execution.
+exclude_paths:
+ - .cache/ # implicit unless exclude_paths is defined in config
+ - test/fixtures/formatting-before/
+ - test/fixtures/formatting-prettier/
+# parseable: true
+# quiet: true
+# strict: true
+# verbosity: 1
+
+# Mock modules or roles in order to pass ansible-playbook --syntax-check
+mock_modules:
+ - zuul_return
+ # note the foo.bar is invalid as being neither a module or a collection
+ - fake_namespace.fake_collection.fake_module
+ - fake_namespace.fake_collection.fake_module.fake_submodule
+mock_roles:
+ - mocked_role
+ - author.role_name # old standalone galaxy role
+ - fake_namespace.fake_collection.fake_role # role within a collection
+
+# Enforce variable names to follow pattern below, in addition to Ansible own
+# requirements, like avoiding python identifiers. To disable add `var-naming`
+# to skip_list.
+var_naming_pattern: "^[a-z_][a-z0-9_]*$"
+
+use_default_rules: true
+# Load custom rules from this specific folder
+# rulesdir:
+# - ./rule/directory/
+
+# Ansible-lint is able to recognize and load skip rules stored inside
+# `.ansible-lint-ignore` (or `.config/ansible-lint-ignore.txt`) files.
+# To skip a rule just enter filename and tag, like "playbook.yml package-latest"
+# on a new line.
+# Optionally you can add comments after the tag, prefixed by "#". We discourage
+# the use of skip_list below because that will hide violations from the output.
+# When putting ignores inside the ignore file, they are marked as ignored, but
+# still visible, making it easier to address later.
+skip_list:
+ - skip_this_tag
+
+# Ansible-lint does not automatically load rules that have the 'opt-in' tag.
+# You must enable opt-in rules by listing each rule 'id' below.
+enable_list:
+ - args
+ - empty-string-compare # opt-in
+ - no-log-password # opt-in
+ - no-same-owner # opt-in
+ - name[prefix] # opt-in
+ # add yaml here if you want to avoid ignoring yaml checks when yamllint
+ # library is missing. Normally its absence just skips using that rule.
+ - yaml
+# Report only a subset of tags and fully ignore any others
+# tags:
+# - jinja[spacing]
+
+# Ansible-lint does not fail on warnings from the rules or tags listed below
+warn_list:
+ - skip_this_tag
+ - experimental # experimental is included in the implicit list
+ # - role-name
+ # - yaml[document-start] # you can also use sub-rule matches
+
+# Some rules can transform files to fix (or make it easier to fix) identified
+# errors. `ansible-lint --write` will reformat YAML files and run these transforms.
+# By default it will run all transforms (effectively `write_list: ["all"]`).
+# You can disable running transforms by setting `write_list: ["none"]`.
+# Or only enable a subset of rule transforms by listing rules/tags here.
+# write_list:
+# - all
+
+# Offline mode disables installation of requirements.yml and schema refreshing
+offline: true
+
+# Define required Ansible's variables to satisfy syntax check
+extra_vars:
+ foo: bar
+ multiline_string_variable: |
+ line1
+ line2
+ complex_variable: ":{;\t$()"
+
+# Uncomment to enforce action validation with tasks, usually is not
+# needed as Ansible syntax check also covers it.
+# skip_action_validation: false
+
+# List of additional kind:pattern to be added at the top of the default
+# match list, first match determines the file kind.
+kinds:
+ # - playbook: "**/examples/*.{yml,yaml}"
+ # - galaxy: "**/folder/galaxy.yml"
+ # - tasks: "**/tasks/*.yml"
+ # - vars: "**/vars/*.yml"
+ # - meta: "**/meta/main.yml"
+ - yaml: "**/*.yaml-too"
+
+# List of additional collections to allow in only-builtins rule.
+# only_builtins_allow_collections:
+# - example_ns.example_collection
+
+# List of additions modules to allow in only-builtins rule.
+# only_builtins_allow_modules:
+# - example_module
+
+# Allow setting custom prefix for name[prefix] rule
+task_name_prefix: "{stem} | "
+
diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg
new file mode 100644
index 0000000..c7e2eec
--- /dev/null
+++ b/ansible/ansible.cfg
@@ -0,0 +1,7 @@
+[defaults]
+host_key_checking = false
+roles_path = ./roles
+playbook_dir = ./playbooks
+
+[ssh_connection]
+pipelining = true
diff --git a/ansible/archi.png b/ansible/archi.png
new file mode 100644
index 0000000..8dc645a
--- /dev/null
+++ b/ansible/archi.png
Binary files differ
diff --git a/ansible/group_vars/all/vault b/ansible/group_vars/all/vault
new file mode 100644
index 0000000..a7efee8
--- /dev/null
+++ b/ansible/group_vars/all/vault
@@ -0,0 +1,51 @@
+$ANSIBLE_VAULT;1.1;AES256
+32386366303436396463376337303465353739633630653431326336613661313938333838353366
+6666393836363265356631343134343337333635363165320a303230366434313563616566666336
+66623133353661386231366635396461363731666434373135396638373166383838323466613833
+3665393664623564620a316431626335646631616232313936333738383536643236343136323364
+35386435376566346435393661633662636435353936323335343337346338633561386638353565
+34656439306537333539666631663266333535646266336532303334343063336230306334663935
+30333732633734363335306237383133633035396339316139306137663535306439656236343035
+61616439316163353437383932353163333363643565386238613961623633333037393462386232
+33653536333637623931356533316664616336363035336432663165373634363435333564653038
+32653665656639313533323336616363623339303765323864356264336637633734333335353537
+36353639343233353638316466656664376363643561323731636437633661666335336235386263
+31653134363735663539396232633532356463656365663563353761303165326630643463383862
+34336435643033386664376563396535363363343338353233323535333466393136623864613965
+36396465393136343739353261333963613339616533656634376234663631646230323362373739
+31313639633931316465356130356432346161316532663864383038333566633639306266646339
+36323834386634376235666130366561383966623335316136663831643561336336333730313262
+34326166636430393334316234363330383962613864323132383463633931656464346531363164
+33633839363563346534636334623334316566643239316535666631366534366237316330386434
+63393234363936303935666462633035353965613136386665333938656232396130666537346630
+36323963303739376439333836643637376364326662303936383661333463343635326263653364
+63383937376435653964383134616433323666373639653136333533303039373063633735383465
+62306366613638633939313437306663616636623836316135386333383864633366613839653035
+36303934373363316132633662396561313431313137366662353733316233396238633564346335
+32346366383536386363663939653535363137613738333361356365386664326536646261333962
+31323737633033623138396339666465373763666434346561633838646635636230613731653337
+30393132386239333033356465663838393665656137316335326233313637643235613735326565
+63373663313533356139383833333135386536306636386233643430663437373837353261653532
+36386436633562386263623938653463323666323766323537623732643839306538623936663466
+65373036356265363063303466333836636431303864653964656534336464386238633562323931
+35636631643230393163366564393763643331616261333835363662306630303636363434336466
+36376133326135616430616139353533363361633937636563393362613032303833333831313430
+36363962623732656461663464353763303135646265613532623665373036663863616334343034
+37623535353438316663396165376432653362666438623233333362383934383564373836653437
+61666335633238323666353638613261343161616463643866343262633861303961363930326466
+64303431346637323761666564616164363833343139333536393039326262626231346638343864
+65663233323362343766613061343061626634303537353237623163356634643761663739393036
+32373465333337623032343064613663646230633038393134373231633664636565643266343034
+36656236323566336538313565346233313363353961313538313639616464306132613863336234
+37316134626439626662313930363335363864623936383563653636313736633564653838306161
+38303163646636616535313666666239646664313962396362356463353765396330326163613662
+61306535303730343436373461306333616462313332313634323464383163643439613864626530
+65386135613334316265333161383337313762383163356239353834653135653231653433663037
+63346335316564633835396630366433383139666465616639666432363462383138336435643332
+34353065613335386436613032633564343535613030643035313064666339346534643061636134
+34626639633039336632626235643763636635663530393536633763353864656530326235306234
+37663439333562343033353336373662346564396631626336356231613564383032636135303364
+32333437343636373136393439613833303231613465613635323164616234653264376134363332
+66353536613861343961343133343837653362363065356430363932353131333363306630333531
+62373864646364363765653931643838646563623438353036386631303664356635313538613561
+3163323132643861393837363333613862666464653434353262
diff --git a/ansible/group_vars/all/vault.example b/ansible/group_vars/all/vault.example
new file mode 100644
index 0000000..3a21a43
--- /dev/null
+++ b/ansible/group_vars/all/vault.example
@@ -0,0 +1,26 @@
+# CHANGE THESE VARS AS PER YOUR DNS SETTINGS!!
+vaultvar_root_domain: 'example.com'
+vaultvar_vault_cname: 'vl' # ie. vl.example.com
+vaultvar_torrents_cname: 'tor'
+vaultvar_sonarr_cname: 'sarr'
+vaultvar_radarr_cname: 'rarr'
+vaultvar_nextcloud_cname: 'nx'
+vaultvar_metrics_cname: 'met'
+
+vaultvar_minio_root_user: 'user'
+vaultvar_minio_root_pass: 'password'
+vaultvar_minio_nas_nfs_path: 'nas:/mnt/pool/set/minio'
+
+vaultvar_namecheap_user: 'username'
+vaultvar_namecheap_api_tok: 'aabbccddeeffgghhiij123456'
+
+vaultvar_torrents_rpc_username: 'username'
+vaultvar_torrents_rpc_password: 'aabbccddeeffgghhiij123456'
+
+vaultvar_nextcloud_nas_nfs_path: 'nas:/mnt/pool/set/nextcloud'
+
+vaultvar_plex_nas_nfs_path: 'nas:/mnt/pool/set/media'
+
+vaultvar_torrents_nas_nfs_path: 'nas:/mnt/pool/set/media'
+
+vaultvar_med_dl_nas_nfs_path: 'nas:/mnt/pool/set/media'
diff --git a/ansible/hosts b/ansible/hosts
new file mode 100644
index 0000000..5b96859
--- /dev/null
+++ b/ansible/hosts
@@ -0,0 +1,29 @@
+# These are just hosts created in the TF section.
+# Replace these hostnames with your host names if required!
+
+[minio]
+minio
+
+[sites]
+sites
+
+[rproxy]
+rproxy
+
+[monitoring]
+monitoring
+
+[nc]
+nc-kumar
+
+[plex]
+plex
+
+[vault]
+vault
+
+[torrents]
+torrents
+
+[med-dl]
+med-dl
diff --git a/ansible/playbooks/init.yaml b/ansible/playbooks/init.yaml
new file mode 100644
index 0000000..7e4029f
--- /dev/null
+++ b/ansible/playbooks/init.yaml
@@ -0,0 +1,98 @@
+---
+
+# ansible-playbook -u root -i 'hosts' --vault-id vaultkey playbooks/init.yaml --tags=vault
+- name: 'Deploy Vault'
+ hosts: 'vault'
+ gather_facts: false
+ roles:
+ - role: 'base'
+ - role: 'docker'
+ - role: 'vault'
+ - role: 'caddy_add'
+ vars:
+ caddy_add_hosts: ['{{ vaultvar_vault_cname }}.{{ vaultvar_root_domain }}']
+ caddy_add_upstream_host: '{{ inventory_hostname }}'
+ caddy_add_upstream_port: 8989
+
+# ansible-playbook -u root -i 'hosts' --vault-id vaultkey playbooks/init.yaml --tags=torrents
+- name: 'Deploy Transmission'
+ hosts: 'torrents'
+ gather_facts: false
+ roles:
+ - role: 'base'
+ - role: 'torrents'
+ vars:
+ torrents_nas_mount_path: '{{ vaultvar_torrents_nas_nfs_path }}'
+ torrents_rpc_username: '{{ vaultvar_torrents_rpc_username }}'
+ torrents_rpc_password: '{{ vaultvar_torrents_rpc_password }}'
+ - role: 'caddy_add'
+ vars:
+ caddy_add_hosts: ['{{ vaultvar_torrents_cname }}.{{ vaultvar_root_domain }}']
+ caddy_add_upstream_host: '{{ inventory_hostname }}'
+
+# ansible-playbook -u root -i 'hosts' --vault-id vaultkey playbooks/init.yaml --tags=med_dl
+- name: 'Deploy Media Downloaders'
+ hosts: 'med-dl'
+ gather_facts: false
+ roles:
+ - role: 'base'
+ - role: 'docker'
+ - role: 'med_dl'
+ vars:
+ med_dl_nas_mount_path: '{{ vaultvar_med_dl_nas_nfs_path }}'
+ - role: 'caddy_add'
+ vars:
+ caddy_add_hosts: ['{{ vaultvar_sonarr_cname }}.{{ vaultvar_root_domain }}']
+ caddy_add_upstream_host: '{{ inventory_hostname }}'
+ caddy_add_upstream_port: 8989
+ - role: 'caddy_add'
+ vars:
+ caddy_add_hosts: ['{{ vaultvar_radarr_cname }}.{{ vaultvar_root_domain }}']
+ caddy_add_upstream_host: '{{ inventory_hostname }}'
+ caddy_add_upstream_port: 7878
+
+# ansible-playbook -u root -i 'hosts' --vault-id vaultkey playbooks/init.yaml --tags=nextcloud
+- name: 'Deploy Nextcloud'
+ hosts: 'nc'
+ gather_facts: false
+ roles:
+ - role: 'base'
+ - role: 'docker'
+ - role: 'nextcloud'
+ vars:
+ nextcloud_nas_mount_path: '{{ vaultvar_nextcloud_nas_nfs_path }}'
+ nextcloud_php_max_mem_mb: 8000
+ nextcloud_php_max_upload_size_gb: 10
+ # You should have DRI devices showing up in your container in `/dev/dri/`.
+ # See the TF readme for steps to enable this.
+ # If not, then set this to false here.
+ # Leave this as a yaml string, it is going to used as a shell var.
+ nextcloud_enable_dri: 'true'
+ - role: 'caddy_add'
+ vars:
+ caddy_add_hosts: ['{{ vaultvar_nextcloud_cname }}.{{ vaultvar_root_domain }}']
+ caddy_add_upstream_host: '{{ inventory_hostname }}'
+ caddy_add_upstream_port: 11000
+
+# ansible-playbook -u root -i 'hosts' --vault-id vaultkey playbooks/init.yaml --tags=plex
+- name: 'Deploy Plex'
+ hosts: 'plex'
+ gather_facts: false
+ roles:
+ - role: 'base'
+ - role: 'plex'
+ vars:
+ plex_nas_mount_path: '{{ vaultvar_plex_nas_nfs_path }}'
+
+# ansible-playbook -u root -i 'hosts' --vault-id vaultkey playbooks/init.yaml --tags=monitoring
+- name: 'Deploy Prometheus'
+ hosts: 'monitoring'
+ gather_facts: false
+ roles:
+ - role: 'base'
+ - role: 'monitoring'
+ - role: 'caddy_add'
+ vars:
+ caddy_add_hosts: ['{{ vaultvar_metrics_cname }}.{{ vaultvar_root_domain }}']
+ caddy_add_upstream_host: '{{ inventory_hostname }}'
+ caddy_add_upstream_port: 9090
diff --git a/ansible/roles/base/tasks/main.yaml b/ansible/roles/base/tasks/main.yaml
new file mode 100644
index 0000000..b5aaf97
--- /dev/null
+++ b/ansible/roles/base/tasks/main.yaml
@@ -0,0 +1,12 @@
+---
+
+- tags: 'always'
+ # noqa: name[missing]
+ block:
+ - name: 'Install some useful packages'
+ ansible.builtin.apt:
+ name:
+ - 'tcpdump'
+ - 'curl'
+ state: 'present'
+ update_cache: true
diff --git a/ansible/roles/caddy_add/tasks/main.yaml b/ansible/roles/caddy_add/tasks/main.yaml
new file mode 100644
index 0000000..80382d4
--- /dev/null
+++ b/ansible/roles/caddy_add/tasks/main.yaml
@@ -0,0 +1,64 @@
+---
+
+- tags: 'always'
+ # noqa: name[missing]
+ block:
+ - name: 'Ensure required vars are set'
+ ansible.builtin.assert:
+ that:
+ - item | mandatory
+ loop:
+ - '{{ caddy_add_hosts }}'
+ - '{{ caddy_add_upstream_host }}'
+ - '{{ caddy_add_upstream_port }}'
+
+ - name: 'Call the Caddy API to add our new host'
+ ansible.builtin.uri:
+ url: 'http://rproxy:2019/config/apps/http/servers/serv0/routes'
+ method: 'PUT'
+ body_format: 'json'
+ status_code: 200
+ body: |
+ {
+ "match": [
+ {
+ "host": {{ caddy_add_hosts | to_json }}
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "handler": "reverse_proxy",
+ "upstreams": [
+ {
+ "dial": "{{ caddy_add_upstream_host }}:{{ caddy_add_upstream_port }}"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ }
+
+ - name: 'Get a copy of the new Caddy config'
+ delegate_to: 'localhost'
+ ansible.builtin.uri:
+ url: 'http://rproxy:2019/config'
+ return_content: true
+ register: '_current_conf'
+
+ - name: 'Save the new config to file'
+ delegate_to: 'localhost'
+ ansible.builtin.copy:
+ dest: 'artifacts/caddy/{{ _file_name }}'
+ content: '{{ _current_conf.json }}'
+ mode: '644'
+ vars:
+ _file_name: 'v{{ lookup("pipe", "date +%Y%m%dT%H%M%S") }}.json'
diff --git a/ansible/roles/docker/tasks/main.yaml b/ansible/roles/docker/tasks/main.yaml
new file mode 100644
index 0000000..bb5b55b
--- /dev/null
+++ b/ansible/roles/docker/tasks/main.yaml
@@ -0,0 +1,34 @@
+---
+
+- tags: 'always'
+ # noqa: name[missing]
+ block:
+ - name: 'Install some prereqs'
+ ansible.builtin.package:
+ name:
+ - 'ca-certificates'
+ - 'gnupg'
+ state: 'present'
+
+ - name: 'Run Docker installer'
+ ansible.builtin.shell:
+ cmd: |
+ curl -fsSL https://get.docker.com | sh
+ creates: '/var/run/docker.sock'
+
+ - name: 'Docker set MTU'
+ ansible.builtin.copy:
+ dest: '/etc/docker/daemon.json'
+ content: |
+ {"mtu": 1420}
+ mode: '644'
+
+ - name: 'Restart Docker service'
+ ansible.builtin.service:
+ name: 'docker'
+ state: 'restarted'
+
+ - name: 'Install docker-compose'
+ ansible.builtin.package:
+ name: 'docker-compose'
+ state: 'present'
diff --git a/ansible/roles/med_dl/tasks/main.yaml b/ansible/roles/med_dl/tasks/main.yaml
new file mode 100644
index 0000000..c4d4a7f
--- /dev/null
+++ b/ansible/roles/med_dl/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+
+- name: 'Run the setup tasks'
+ ansible.builtin.import_tasks: 'setup.yaml'
+ tags: 'med_dl'
diff --git a/ansible/roles/med_dl/tasks/setup.yaml b/ansible/roles/med_dl/tasks/setup.yaml
new file mode 100644
index 0000000..46affac
--- /dev/null
+++ b/ansible/roles/med_dl/tasks/setup.yaml
@@ -0,0 +1,38 @@
+---
+
+- name: 'setup | Ensure required vars are set'
+ ansible.builtin.assert:
+ that:
+ - item | mandatory
+ loop:
+ - '{{ med_dl_nas_mount_path }}'
+ no_log: true
+
+- name: 'setup | Install required pkgs'
+ ansible.builtin.package:
+ name:
+ - 'nfs-common'
+
+- name: 'setup | Create the mount dir'
+ ansible.builtin.file:
+ path: '/mnt/data'
+ state: 'directory'
+ mode: '755'
+
+- name: 'setup | Setup the NFS mount'
+ ansible.posix.mount:
+ src: '{{ med_dl_nas_mount_path }}'
+ path: '/mnt/data'
+ state: 'mounted'
+ fstype: 'nfs'
+
+- name: 'setup | Copy the docker-compose file'
+ ansible.builtin.template:
+ src: 'docker-compose.yaml.j2'
+ dest: 'docker-compose.yaml'
+ mode: '644'
+
+- name: 'setup | Start the service'
+ ansible.builtin.command: 'docker-compose up -d'
+ register: '_compose_up_cmd'
+ changed_when: '_compose_up_cmd.rc == 0'
diff --git a/ansible/roles/med_dl/templates/docker-compose.yaml.j2 b/ansible/roles/med_dl/templates/docker-compose.yaml.j2
new file mode 100644
index 0000000..fa25db0
--- /dev/null
+++ b/ansible/roles/med_dl/templates/docker-compose.yaml.j2
@@ -0,0 +1,67 @@
+{{ ansible_managed | comment }}
+version: "3"
+services:
+ prowlarr:
+ image: lscr.io/linuxserver/prowlarr:latest
+ restart: unless-stopped
+ container_name: prowlarr
+ volumes:
+ - prowlarr-config:/config
+ ports:
+ - 9696:9696
+ environment:
+ - PUID=1001
+ - PGID=1001
+ - TZ=Etc/UTC
+ networks:
+ - med-dl
+
+ radarr:
+ image: lscr.io/linuxserver/radarr:latest
+ restart: unless-stopped
+ container_name: radarr
+ volumes:
+ - radarr-config:/config
+ - /mnt/data/downloads:/mnt/data/downloads
+ - /mnt/data/mov:/mov
+ ports:
+ - 7878:7878
+ environment:
+ - PUID=1001
+ - PGID=1001
+ - TZ=Etc/UTC
+ networks:
+ - med-dl
+
+ sonarr:
+ image: lscr.io/linuxserver/sonarr:latest
+ restart: unless-stopped
+ container_name: sonarr
+ volumes:
+ - sonarr-config:/config
+ - /mnt/data/downloads:/mnt/data/downloads
+ - /mnt/data/tv:/tv
+ ports:
+ - 8989:8989
+ environment:
+ - PUID=1001
+ - PGID=1001
+ - TZ=Etc/UTC
+ networks:
+ - med-dl
+
+volumes:
+ prowlarr-config:
+ name: prowlarr-config
+ radarr-config:
+ name: radarr-config
+ sonarr-config:
+ name: sonarr-config
+
+networks:
+ med-dl:
+ name: med-dl
+ driver: bridge
+ driver_opts:
+ com.docker.network.driver.mtu: 1420
+
diff --git a/ansible/roles/monitoring/tasks/main.yaml b/ansible/roles/monitoring/tasks/main.yaml
new file mode 100644
index 0000000..256a09f
--- /dev/null
+++ b/ansible/roles/monitoring/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+
+- name: 'Run the setup tasks'
+ ansible.builtin.import_tasks: 'setup.yaml'
+ tags: 'monitoring'
diff --git a/ansible/roles/monitoring/tasks/setup.yaml b/ansible/roles/monitoring/tasks/setup.yaml
new file mode 100644
index 0000000..824d2db
--- /dev/null
+++ b/ansible/roles/monitoring/tasks/setup.yaml
@@ -0,0 +1,20 @@
+---
+
+- name: 'setup | Install required pkgs'
+ ansible.builtin.package:
+ name:
+ - 'prometheus'
+ - 'prometheus-node-exporter'
+ - 'prometheus-blackbox-exporter'
+
+- name: 'setup | Set the Prom config'
+ ansible.builtin.template:
+ src: 'prometheus.yml.j2'
+ dest: '/etc/prometheus/prometheus.yml'
+ mode: '644'
+
+- name: 'setup | Start the service'
+ ansible.builtin.service:
+ name: 'prometheus'
+ state: 'restarted'
+ enabled: true
diff --git a/ansible/roles/monitoring/templates/prometheus.yml.j2 b/ansible/roles/monitoring/templates/prometheus.yml.j2
new file mode 100644
index 0000000..a5f3b46
--- /dev/null
+++ b/ansible/roles/monitoring/templates/prometheus.yml.j2
@@ -0,0 +1,47 @@
+{{ ansible_managed | comment }}
+global:
+ scrape_interval: 1m
+ evaluation_interval: 1m
+ # scrape_timeout is set to the global default (10s).
+
+ # Attach these labels to any time series or alerts when communicating with
+ # external systems (federation, remote storage, Alertmanager).
+ external_labels:
+ monitor: 'example'
+
+# Alertmanager configuration
+alerting:
+ alertmanagers:
+ - static_configs:
+ - targets: ['localhost:9093']
+
+# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
+rule_files:
+ # - "first_rules.yml"
+ # - "second_rules.yml"
+
+# A scrape configuration containing exactly one endpoint to scrape:
+# Here it's Prometheus itself.
+scrape_configs:
+ # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
+ - job_name: 'prometheus'
+
+ # Override the global default and scrape targets from this job every 5 seconds.
+ # scrape_interval: 30s
+ # scrape_timeout: 30s
+
+ # metrics_path defaults to '/metrics'
+ # scheme defaults to 'http'.
+
+ static_configs:
+ - targets: ['localhost:9090']
+
+ - job_name: node
+ # If prometheus-node-exporter is installed, grab stats about the local
+ # machine by default.
+ static_configs:
+ - targets: ['localhost:9100']
+
+ - job_name: caddy
+ static_configs:
+ - targets: ['rproxy:2019', 'sites:2019']
diff --git a/ansible/roles/nextcloud/tasks/main.yaml b/ansible/roles/nextcloud/tasks/main.yaml
new file mode 100644
index 0000000..0d3697b
--- /dev/null
+++ b/ansible/roles/nextcloud/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+
+- name: 'Run the setup tasks'
+ ansible.builtin.import_tasks: 'setup.yaml'
+ tags: 'nextcloud'
diff --git a/ansible/roles/nextcloud/tasks/setup.yaml b/ansible/roles/nextcloud/tasks/setup.yaml
new file mode 100644
index 0000000..ac17702
--- /dev/null
+++ b/ansible/roles/nextcloud/tasks/setup.yaml
@@ -0,0 +1,40 @@
+---
+
+- name: 'setup | Ensure required vars are set'
+ ansible.builtin.assert:
+ that:
+ - item | mandatory
+ loop:
+ - '{{ nextcloud_nas_mount_path }}'
+ - '{{ nextcloud_php_max_mem_mb }}'
+ - '{{ nextcloud_php_max_upload_size_gb }}'
+ no_log: true
+
+- name: 'setup | Install required pkgs'
+ ansible.builtin.package:
+ name:
+ - 'nfs-common'
+
+- name: 'setup | Create the mount dir'
+ ansible.builtin.file:
+ path: '/mnt/data'
+ state: 'directory'
+ mode: '755'
+
+- name: 'setup | Setup the NFS mount'
+ ansible.posix.mount:
+ src: '{{ nextcloud_nas_mount_path }}'
+ path: '/mnt/data'
+ state: 'mounted'
+ fstype: 'nfs'
+
+- name: 'setup | Copy the docker-compose file'
+ ansible.builtin.template:
+ src: 'docker-compose.yaml.j2'
+ dest: 'docker-compose.yaml'
+ mode: '644'
+
+- name: 'setup | Start the service'
+ ansible.builtin.command: 'docker-compose up -d'
+ register: '_compose_up_cmd'
+ changed_when: '_compose_up_cmd.rc == 0'
diff --git a/ansible/roles/nextcloud/templates/docker-compose.yaml.j2 b/ansible/roles/nextcloud/templates/docker-compose.yaml.j2
new file mode 100644
index 0000000..3571432
--- /dev/null
+++ b/ansible/roles/nextcloud/templates/docker-compose.yaml.j2
@@ -0,0 +1,41 @@
+{{ ansible_managed | comment }}
+version: "3"
+services:
+ nextcloud:
+ image: nextcloud/all-in-one:latest
+ restart: always
+ container_name: nextcloud-aio-mastercontainer
+ volumes:
+ - nextcloud_aio_mastercontainer:/mnt/docker-aio-config
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ ports:
+ - 8080:8080
+ environment:
+ - APACHE_PORT=11000
+ - APACHE_IP_BINDING=0.0.0.0
+ - COLLABORA_SECCOMP_DISABLED=true
+ - NEXTCLOUD_DATADIR=/mnt/data
+ - NEXTCLOUD_MOUNT=/mnt/
+ - NEXTCLOUD_UPLOAD_LIMIT={{ nextcloud_php_max_upload_size_gb }}G
+ - NEXTCLOUD_MAX_TIME=3600
+ - NEXTCLOUD_MEMORY_LIMIT={{ nextcloud_php_max_mem_mb }}M
+ - NEXTCLOUD_STARTUP_APPS=deck twofactor_totp tasks calendar contacts notes memories richdocuments -photos
+ - NEXTCLOUD_ADDITIONAL_APKS=imagemagick
+ - NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS=imagick
+ - NEXTCLOUD_ENABLE_DRI_DEVICE={{ nextcloud_enable_dri }}
+ - TALK_PORT=3478
+ - WATCHTOWER_DOCKER_SOCKET_PATH=/var/run/docker.sock
+ - SKIP_DOMAIN_VALIDATION=true
+ networks:
+ - nextcloud-aio
+
+volumes:
+ nextcloud_aio_mastercontainer:
+ name: nextcloud_aio_mastercontainer
+
+networks:
+ nextcloud-aio:
+ name: nextcloud-aio
+ driver: bridge
+ driver_opts:
+ com.docker.network.driver.mtu: 1420
diff --git a/ansible/roles/plex/tasks/main.yaml b/ansible/roles/plex/tasks/main.yaml
new file mode 100644
index 0000000..5469534
--- /dev/null
+++ b/ansible/roles/plex/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+
+- name: 'Run the setup tasks'
+ ansible.builtin.import_tasks: 'setup.yaml'
+ tags: 'plex'
diff --git a/ansible/roles/plex/tasks/setup.yaml b/ansible/roles/plex/tasks/setup.yaml
new file mode 100644
index 0000000..de0eb69
--- /dev/null
+++ b/ansible/roles/plex/tasks/setup.yaml
@@ -0,0 +1,55 @@
+---
+
+- name: 'setup | Ensure required vars are set'
+ ansible.builtin.assert:
+ that:
+ - item | mandatory
+ loop:
+ - '{{ plex_nas_mount_path }}'
+ no_log: true
+
+- name: 'setup | Install required pkgs'
+ ansible.builtin.package:
+ name:
+ - 'nfs-common'
+ - 'curl'
+ - 'gnupg'
+
+- name: 'setup | Create the mount dir'
+ ansible.builtin.file:
+ path: '/mnt/data'
+ state: 'directory'
+ mode: '755'
+
+- name: 'setup | Setup the NFS mount'
+ ansible.posix.mount:
+ src: '{{ plex_nas_mount_path }}'
+ path: '/mnt/data'
+ state: 'mounted'
+ fstype: 'nfs'
+
+- name: 'setup | Get the Plex signed keys'
+ ansible.builtin.shell:
+ # noqa: risky-shell-pipe command-instead-of-module
+ cmd: >
+ curl -sS https://downloads.plex.tv/plex-keys/PlexSign.key | gpg --dearmor | tee /usr/share/keyrings/plex.gpg > /dev/null
+ creates: '/usr/share/keyrings/plex.gpg'
+
+- name: 'setup | Set the Plex sources'
+ ansible.builtin.copy:
+ dest: '/etc/apt/sources.list.d/plexmediaserver.list'
+ content: |
+ deb [signed-by=/usr/share/keyrings/plex.gpg] https://downloads.plex.tv/repo/deb public main
+ mode: '644'
+
+- name: 'setup | Install the package'
+ ansible.builtin.apt:
+ name: 'plexmediaserver'
+ state: 'present'
+ update_cache: true
+
+- name: 'setup | Start the service'
+ ansible.builtin.service:
+ name: 'plexmediaserver'
+ state: 'stopped'
+ enabled: true
diff --git a/ansible/roles/torrents/tasks/main.yaml b/ansible/roles/torrents/tasks/main.yaml
new file mode 100644
index 0000000..806a4e3
--- /dev/null
+++ b/ansible/roles/torrents/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+
+- name: 'Run the setup tasks'
+ ansible.builtin.import_tasks: 'setup.yaml'
+ tags: 'torrents'
diff --git a/ansible/roles/torrents/tasks/setup.yaml b/ansible/roles/torrents/tasks/setup.yaml
new file mode 100644
index 0000000..25f1fe8
--- /dev/null
+++ b/ansible/roles/torrents/tasks/setup.yaml
@@ -0,0 +1,79 @@
+---
+
+- name: 'setup | Ensure required vars are set'
+ ansible.builtin.assert:
+ that:
+ - item | mandatory
+ loop:
+ - '{{ torrents_nas_mount_path }}'
+ - '{{ torrents_rpc_username }}'
+ - '{{ torrents_rpc_password }}'
+ no_log: true
+
+- name: 'setup | Install required pkgs'
+ ansible.builtin.package:
+ name:
+ - 'nfs-common'
+ - 'transmission-daemon'
+
+- name: 'setup | Stop the transmission-daemon service'
+ ansible.builtin.service:
+ name: 'transmission-daemon'
+ state: 'stopped'
+
+- name: 'setup | Create the mount dir'
+ ansible.builtin.file:
+ path: '/mnt/data'
+ state: 'directory'
+ mode: '755'
+
+- name: 'setup | Setup the NFS mount'
+ ansible.posix.mount:
+ src: '{{ torrents_nas_mount_path }}'
+ path: '/mnt/data'
+ state: 'mounted'
+ fstype: 'nfs'
+
+- name: 'setup | Create new user for transmission'
+ ansible.builtin.user:
+ name: 'transmission-user'
+ uid: 1001
+ groups: 'users'
+ create_home: true
+
+- name: 'setup | Fix the service file to use our user'
+ ansible.builtin.lineinfile:
+ path: '/lib/systemd/system/transmission-daemon.service'
+ regexp: '^User='
+ line: 'User=transmission-user'
+
+- name: 'setup | Reload systemd daemon'
+ ansible.builtin.systemd:
+ daemon_reload: true
+
+# THis is kind of a hack to force the generation of
+# /home/transmission-user/.config/transmission-daemon dirs
+# so that we can place our config file there.
+- name: 'setup | Start the service'
+ ansible.builtin.service:
+ name: 'transmission-daemon'
+ state: 'started'
+
+# We still need to stop it to place the config file or else it will get overwritten
+- name: 'setup | Stop the service'
+ ansible.builtin.service:
+ name: 'transmission-daemon'
+ state: 'stopped'
+
+- name: 'setup | Copy the transmission config'
+ ansible.builtin.template:
+ src: 'settings.json.j2'
+ dest: '/home/transmission-user/.config/transmission-daemon/settings.json'
+ owner: 'transmission-user'
+ mode: '644'
+
+- name: 'setup | Start the service'
+ ansible.builtin.service:
+ name: 'transmission-daemon'
+ state: 'started'
+ enabled: true
diff --git a/ansible/roles/torrents/templates/settings.json.j2 b/ansible/roles/torrents/templates/settings.json.j2
new file mode 100644
index 0000000..cf5ea4b
--- /dev/null
+++ b/ansible/roles/torrents/templates/settings.json.j2
@@ -0,0 +1,73 @@
+{
+ "alt-speed-down": 50,
+ "alt-speed-enabled": false,
+ "alt-speed-time-begin": 540,
+ "alt-speed-time-day": 127,
+ "alt-speed-time-enabled": false,
+ "alt-speed-time-end": 1020,
+ "alt-speed-up": 50,
+ "bind-address-ipv4": "0.0.0.0",
+ "bind-address-ipv6": "::",
+ "blocklist-enabled": false,
+ "blocklist-url": "http://www.example.com/blocklist",
+ "cache-size-mb": 4,
+ "dht-enabled": true,
+ "download-dir": "/mnt/data/downloads",
+ "download-limit": 100,
+ "download-limit-enabled": 0,
+ "download-queue-enabled": true,
+ "download-queue-size": 5,
+ "encryption": 1,
+ "idle-seeding-limit": 30,
+ "idle-seeding-limit-enabled": false,
+ "incomplete-dir": "/home/transmission-user/downloads",
+ "incomplete-dir-enabled": false,
+ "lpd-enabled": false,
+ "max-peers-global": 200,
+ "message-level": 1,
+ "peer-congestion-algorithm": "",
+ "peer-id-ttl-hours": 6,
+ "peer-limit-global": 200,
+ "peer-limit-per-torrent": 50,
+ "peer-port": 51413,
+ "peer-port-random-high": 65535,
+ "peer-port-random-low": 49152,
+ "peer-port-random-on-start": false,
+ "peer-socket-tos": "default",
+ "pex-enabled": true,
+ "port-forwarding-enabled": false,
+ "preallocation": 1,
+ "prefetch-enabled": true,
+ "queue-stalled-enabled": true,
+ "queue-stalled-minutes": 30,
+ "ratio-limit": 2,
+ "ratio-limit-enabled": false,
+ "rename-partial-files": true,
+ "rpc-authentication-required": true,
+ "rpc-bind-address": "0.0.0.0",
+ "rpc-enabled": true,
+ "rpc-host-whitelist": "",
+ "rpc-host-whitelist-enabled": true,
+ "rpc-password": "{{ torrents_rpc_password }}",
+ "rpc-port": 9091,
+ "rpc-url": "/transmission/",
+ "rpc-username": "{{ torrents_rpc_username }}",
+ "rpc-whitelist": "*",
+ "rpc-whitelist-enabled": true,
+ "scrape-paused-torrents-enabled": true,
+ "script-torrent-done-enabled": false,
+ "script-torrent-done-filename": "",
+ "seed-queue-enabled": false,
+ "seed-queue-size": 10,
+ "speed-limit-down": 100,
+ "speed-limit-down-enabled": false,
+ "speed-limit-up": 100,
+ "speed-limit-up-enabled": false,
+ "start-added-torrents": true,
+ "trash-original-torrent-files": false,
+ "umask": 18,
+ "upload-limit": 100,
+ "upload-limit-enabled": 0,
+ "upload-slots-per-torrent": 14,
+ "utp-enabled": true
+}
diff --git a/ansible/roles/vault/tasks/main.yaml b/ansible/roles/vault/tasks/main.yaml
new file mode 100644
index 0000000..191bbc1
--- /dev/null
+++ b/ansible/roles/vault/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+
+- name: 'Run the setup tasks'
+ ansible.builtin.import_tasks: 'setup.yaml'
+ tags: 'vault'
diff --git a/ansible/roles/vault/tasks/setup.yaml b/ansible/roles/vault/tasks/setup.yaml
new file mode 100644
index 0000000..f05aeee
--- /dev/null
+++ b/ansible/roles/vault/tasks/setup.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: 'setup | Copy the docker-compose file'
+ ansible.builtin.template:
+ src: 'docker-compose.yaml.j2'
+ dest: 'docker-compose.yaml'
+ mode: '644'
+
+- name: 'setup | Start the service'
+ ansible.builtin.command: 'docker-compose up -d'
+ register: '_compose_up_cmd'
+ changed_when: '_compose_up_cmd.rc == 0'
diff --git a/ansible/roles/vault/templates/docker-compose.yaml.j2 b/ansible/roles/vault/templates/docker-compose.yaml.j2
new file mode 100644
index 0000000..7c9d74c
--- /dev/null
+++ b/ansible/roles/vault/templates/docker-compose.yaml.j2
@@ -0,0 +1,28 @@
+{{ ansible_managed | comment }}
+version: "3"
+services:
+ vaultwarden:
+ image: vaultwarden/server:latest
+ restart: always
+ container_name: vaultwarden
+ volumes:
+ - vaultwarden:/data
+ ports:
+ - 8989:80
+ environment:
+ - WEBSOCKET_ENABLED=true
+ - INVITATIONS_ALLOWED=true
+ - SIGNUPS_ALLOWED=true
+ networks:
+ - vaultwarden
+
+volumes:
+ vaultwarden:
+ name: vaultwarden
+
+networks:
+ vaultwarden:
+ name: vaultwarden
+ driver: bridge
+ driver_opts:
+ com.docker.network.driver.mtu: 1420
diff --git a/terraform/README.md b/terraform/README.md
new file mode 100644
index 0000000..11c1762
--- /dev/null
+++ b/terraform/README.md
@@ -0,0 +1,42 @@
+This part of the repo is responsible for creating
+our home-lab infrastructure using Terraform.
+
+# First time
+```
+terraform init
+cp con.env.example con.env
+```
+Modify `con.env` with your Proxmox access creds.
+
+> Due to [this](https://github.com/bpg/terraform-provider-proxmox/issues/344) issue,
+we cannot use the recommended approach dedicated TF user's API access.
+
+# Usual flow
+```
+source con.env
+
+terraform plan -out=tfplan
+#terraform plan -target="proxmox_lxc.testvm" -out=tfplan
+
+terraform apply "tfplan"
+```
+
+# Teardown
+```
+terraform destroy
+terraform destroy -target="proxmox_lxc.testvm"
+```
+
+# A note about GPU pass-through
+At the moment, this provider does not support passing through the Host's GPU or iGPU
+to the guest VM/LXC. So, if you have Guests that require this, you need to do it manually
+by adding the following config on Proxmox `/etc/pve/lxc/<id of your container>.conf`:
+```
+lxc.cgroup2.devices.allow: c 226:0 rwm
+lxc.cgroup2.devices.allow: c 226:128 rwm
+lxc.cgroup2.devices.allow: c 29:0 rwm
+lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
+lxc.mount.entry: /dev/dri/renderD128 dev/renderD128 none bind,optional,create=file
+```
+
+> I do this for my Nextcloud, and Plex containers.
diff --git a/terraform/con.env.example b/terraform/con.env.example
new file mode 100644
index 0000000..a1ddc29
--- /dev/null
+++ b/terraform/con.env.example
@@ -0,0 +1,3 @@
+export PM_API_URL="https://<ip of your proxmox host>:8006/api2/json"
+export PM_USER="root@pam"
+export PM_PASS="aabbccdd123"
diff --git a/terraform/main.tf b/terraform/main.tf
new file mode 100644
index 0000000..f286985
--- /dev/null
+++ b/terraform/main.tf
@@ -0,0 +1,309 @@
+resource "proxmox_lxc" "minio" {
+ target_node = "pve"
+ hostname = "minio"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.minio_password}"
+ unprivileged = false
+ cores = 1
+ cpulimit = 2
+ memory = 2048
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "3G"
+ }
+
+ features {
+ mount = "nfs;"
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "sites" {
+ target_node = "pve"
+ hostname = "sites"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.sites_password}"
+ unprivileged = true
+ cores = 1
+ cpulimit = 1
+ memory = 1024
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "3G"
+ }
+
+ features {
+ fuse = true
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "rproxy" {
+ target_node = "pve"
+ hostname = "rproxy"
+ ostemplate = "local:vztmpl/alpine-3.18-default_20230607_amd64.tar.xz"
+ password = "${var.rproxy_password}"
+ unprivileged = true
+ cores = 1
+ cpulimit = 1
+ memory = 1024
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "2G"
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "monitoring" {
+ target_node = "pve"
+ hostname = "monitoring"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.monitoring_password}"
+ unprivileged = false
+ cores = 1
+ cpulimit = 2
+ memory = 1024
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "10G"
+ }
+
+ features {
+ nesting = true
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "nc_kumar" {
+ target_node = "pve"
+ hostname = "nc-kumar"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.nc_kumar_password}"
+ unprivileged = false
+ cores = 8
+ cpulimit = 8
+ memory = 12288
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "15G"
+ }
+
+ features {
+ nesting = true
+ fuse = true
+ mount = "nfs;"
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "plex" {
+ target_node = "pve"
+ hostname = "plex"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.plex_password}"
+ unprivileged = false
+ cores = 8
+ cpulimit = 8
+ memory = 4096
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "100G"
+ }
+
+ features {
+ nesting = true
+ fuse = true
+ mount = "nfs;"
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "vault" {
+ target_node = "pve"
+ hostname = "vault"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.vault_password}"
+ unprivileged = true
+ cores = 1
+ cpulimit = 2
+ memory = 1024
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "10G"
+ }
+
+ features {
+ nesting = true
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "torrents" {
+ target_node = "pve"
+ hostname = "torrents"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.torrents_password}"
+ unprivileged = false
+ cores = 2
+ cpulimit = 2
+ memory = 2048
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "100G"
+ }
+
+ features {
+ nesting = true
+ fuse = true
+ mount = "nfs;"
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
+
+resource "proxmox_lxc" "med_dl" {
+ target_node = "pve"
+ hostname = "med-dl"
+ ostemplate = "local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
+ password = "${var.med_dl_password}"
+ unprivileged = false
+ cores = 4
+ cpulimit = 4
+ memory = 2048
+ start = true
+ onboot = true
+
+ ssh_public_keys = <<-EOT
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9ONfhaHpY0bSk3o81H/eEp+UxQ9RCszJ7p3f+rvcpH kdam0@art-sr
+ EOT
+
+ rootfs {
+ storage = "local-lvm"
+ size = "10G"
+ }
+
+ features {
+ nesting = true
+ fuse = true
+ mount = "nfs;"
+ }
+
+ network {
+ name = "eth0"
+ bridge = "vmbr1"
+ tag = 30
+ ip = "dhcp"
+ mtu = "1420"
+ }
+}
diff --git a/terraform/terraform.tf b/terraform/terraform.tf
new file mode 100644
index 0000000..64842b0
--- /dev/null
+++ b/terraform/terraform.tf
@@ -0,0 +1,12 @@
+terraform {
+ required_providers {
+ proxmox = {
+ source = "telmate/proxmox"
+ version = ">=1.0.0"
+ }
+ }
+}
+
+provider "proxmox" {
+ # Configuration options
+}
diff --git a/terraform/variables.tf b/terraform/variables.tf
new file mode 100644
index 0000000..00b96c8
--- /dev/null
+++ b/terraform/variables.tf
@@ -0,0 +1,45 @@
+variable "minio_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for minio"
+}
+variable "sites_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for sites"
+}
+variable "rproxy_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for rproxy"
+}
+variable "monitoring_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for monitoring"
+}
+variable "nc_kumar_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for nc_kumar"
+}
+variable "plex_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for plex"
+}
+variable "vault_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for vault"
+}
+variable "torrents_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for torrents"
+}
+variable "med_dl_password" {
+ type = string
+ sensitive = true
+ description = "Secret key for med_dl"
+}