├── .gitignore ├── requirements.yml ├── ansible.cfg ├── roles ├── docker │ └── tasks │ │ ├── main.yml │ │ ├── install.yml │ │ └── dirs.yml ├── system │ └── tasks │ │ ├── main.yml │ │ ├── essential.yml │ │ └── user.yml └── services │ └── tasks │ ├── watchtower.yml │ ├── main.yml │ ├── mysql.yml │ ├── guacamole.yml │ ├── dashdot.yml │ ├── n8n.yml │ ├── requestrr.yml │ ├── uptime_kuma.yml │ ├── vaultwarden.yml │ ├── homarr.yml │ ├── prowlarr.yml │ ├── jellyseerr.yml │ ├── homeassistant.yml │ ├── radarr.yml │ ├── sonarr.yml │ ├── portainer.yml │ ├── unmanic.yml │ ├── duplicati.yml │ ├── jellyfin.yml │ ├── syncthing.yml │ ├── code_server.yml │ ├── wireguard.yml │ ├── pihole.yml │ ├── filebrowser.yml │ ├── nextcloud.yml │ ├── transmission.yml │ ├── finalcheck.yml │ ├── docker.yml │ ├── traefik.yml │ ├── monitoring.yml │ └── authelia.yml ├── postinstallation.md ├── hosts └── hosts ├── run.yml ├── serviceslist.md ├── tasks └── ssh_port_test.yml ├── group_vars └── all │ └── vars.yml ├── variablehelp.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | roles: 3 | - name: geerlingguy.security 4 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | INVENTORY = hosts 3 | 4 | [ssh_connection] 5 | pipelining = true -------------------------------------------------------------------------------- /roles/docker/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: dirs.yml 2 | - include_tasks: install.yml 3 | -------------------------------------------------------------------------------- /roles/system/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: essential.yml 2 | - include_tasks: user.yml 3 | -------------------------------------------------------------------------------- /postinstallation.md: -------------------------------------------------------------------------------- 1 | # Post Installation Help 2 | 3 | This is a guide to help you set up services and troubleshoot any issues you may have with service configuration. 4 | -------------------------------------------------------------------------------- /hosts/hosts: -------------------------------------------------------------------------------- 1 | [homeserver] 2 | homeserver 3 | 4 | [homeserver:vars] 5 | ansible_host= 6 | ansible_user= 7 | ansible_connection=ssh 8 | ansible_ssh_private_key_file= 9 | ansible_port=69 -------------------------------------------------------------------------------- /run.yml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | gather_facts: no 3 | 4 | pre_tasks: 5 | - import_tasks: tasks/ssh_port_test.yml 6 | 7 | - hosts: all 8 | become: yes 9 | 10 | roles: 11 | - role: system 12 | 13 | - role: docker 14 | 15 | - role: services 16 | 17 | - role: geerlingguy.security 18 | -------------------------------------------------------------------------------- /roles/services/tasks/watchtower.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create watchtower container 3 | community.docker.docker_container: 4 | name: watchtower 5 | image: containrrr/watchtower 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "/var/run/docker.sock:/var/run/docker.sock" 11 | env: 12 | WATCHTOWER_CLEANUP: "true" 13 | networks: 14 | - name: homelab 15 | -------------------------------------------------------------------------------- /serviceslist.md: -------------------------------------------------------------------------------- 1 | # Services List 2 | 3 | These playbooks set up the follow services: 4 | 5 | - Authelia 6 | - Traefik 7 | - Code Server 8 | - Dashdot 9 | - Duplicati 10 | - Filebrowser 11 | - Guacamole 12 | - Homarr 13 | - Homeassistant 14 | - Jellyfin 15 | - Jellyseerr 16 | - Monitoring (grafana and prometheus) 17 | - Mysql 18 | - n8n 19 | - Nextcloud 20 | - Pihole + Unbound 21 | - Portainer 22 | - Prowlarr 23 | - Radarr 24 | - Requestrr 25 | - Sonarr 26 | - Syncthing 27 | - Unmanic 28 | - Uptime_kuma 29 | - Transmission 30 | - Vaultwarden (formerly Bitwarden_rs) 31 | - Watchtower 32 | - Wireguard 33 | -------------------------------------------------------------------------------- /roles/services/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Deploy docker apps 2 | ansible.builtin.include_tasks: "roles/services/tasks/{{ item }}.yml" 3 | loop: 4 | - authelia 5 | - traefik 6 | - code_server 7 | - dashdot 8 | - duplicati 9 | - filebrowser 10 | - guacamole 11 | - homarr 12 | - homeassistant 13 | - jellyfin 14 | - jellyseerr 15 | - monitoring 16 | - mysql 17 | - n8n 18 | - nextcloud 19 | - pihole 20 | - portainer 21 | - prowlarr 22 | - radarr 23 | - requestrr 24 | - sonarr 25 | - syncthing 26 | - unmanic 27 | - uptime_kuma 28 | - transmission 29 | - vaultwarden 30 | - watchtower 31 | - wireguard 32 | 33 | - include_tasks: finalcheck.yml 34 | -------------------------------------------------------------------------------- /roles/services/tasks/mysql.yml: -------------------------------------------------------------------------------- 1 | - name: Create MySQL container 2 | community.docker.docker_container: 3 | name: mysql 4 | image: mysql:latest 5 | env: 6 | PUID: "{{ puid }}" 7 | PGID: "{{ pgid }}" 8 | MYSQL_ROOT_PASSWORD: "{{ mysql_password }}" 9 | MYSQL_DATABASE: "admin" 10 | MYSQL_USER: "admin" 11 | MYSQL_PASSWORD: "{{ mysql_password }}" 12 | state: started 13 | networks: 14 | - name: homelab 15 | volumes: 16 | - "{{ docker_dir }}/mysql/config:/etc/mysql/conf.d" 17 | - "{{ data_dir }}/mysql/database:/var/lib/mysql" 18 | - "{{ data_dir }}/mysql/dbbackups:/var/backups/mysql" 19 | 20 | restart_policy: unless-stopped 21 | labels: 22 | traefik.enable: "true" 23 | traefik.docker.network: "homelab" 24 | -------------------------------------------------------------------------------- /roles/services/tasks/guacamole.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create guacamole container 3 | community.docker.docker_container: 4 | name: guacamole 5 | image: maxwaldorf/guacamole 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "{{ docker_dir }}/guacamole/config:/config" 11 | networks: 12 | - name: homelab 13 | labels: 14 | traefik.enable: "true" 15 | traefik.http.routers.guac.entrypoints: "http" 16 | traefik.http.routers.guac.rule: "Host(`guac.{{ domain }}`)" 17 | traefik.http.middlewares.guac-https-redirect.redirectscheme.scheme: "https" 18 | traefik.http.routers.guac.middlewares: "guac-https-redirect" 19 | traefik.http.routers.guac-secure.entrypoints: "https" 20 | traefik.http.routers.guac-secure.rule: "Host(`guac.{{ domain }}`)" 21 | traefik.http.routers.guac-secure.tls: "true" 22 | traefik.http.routers.guac-secure.service: "guac" 23 | traefik.http.routers.guac-secure.middlewares: "authelia@docker" 24 | traefik.http.services.guac.loadbalancer.server.port: "8080" 25 | traefik.docker.network: "homelab" 26 | -------------------------------------------------------------------------------- /roles/services/tasks/dashdot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create dashdot container 3 | community.docker.docker_container: 4 | name: dashdot 5 | image: mauricenino/dashdot:latest 6 | restart_policy: unless-stopped 7 | state: started 8 | pull: true 9 | privileged: true 10 | networks: 11 | - name: homelab 12 | volumes: 13 | - "/:/mnt/host:ro" 14 | env: 15 | DASHDOT_ACCEPT_OOKLA_EULA: "true" 16 | labels: 17 | traefik.enable: "true" 18 | traefik.http.routers.dash.entrypoints: "http" 19 | traefik.http.routers.dash.rule: "Host(`dash.{{ domain }}`)" 20 | traefik.http.middlewares.dash-https-redirect.redirectscheme.scheme: "https" 21 | traefik.http.routers.dash.middlewares: "dash-https-redirect" 22 | traefik.http.routers.dash-secure.entrypoints: "https" 23 | traefik.http.routers.dash-secure.rule: "Host(`dash.{{ domain }}`)" 24 | traefik.http.routers.dash-secure.tls: "true" 25 | traefik.http.routers.dash-secure.service: "dash" 26 | traefik.http.routers.dash-secure.middlewares: "authelia@docker" 27 | traefik.http.services.dash.loadbalancer.server.port: "3001" 28 | traefik.docker.network: "homelab" 29 | -------------------------------------------------------------------------------- /roles/services/tasks/n8n.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create n8n container 3 | community.docker.docker_container: 4 | name: n8n 5 | image: docker.n8n.io/n8nio/n8n 6 | state: started 7 | restart_policy: unless-stopped 8 | pull: true 9 | volumes: 10 | - "{{ docker_dir }}/n8n:/home/node/.n8n" 11 | networks: 12 | - name: homelab 13 | env: 14 | GENERIC_TIMEZONE: "{{ timezone }}" 15 | TZ: "{{ timezone }}" 16 | labels: 17 | traefik.enable: "true" 18 | traefik.http.routers.n8n.entrypoints: "http" 19 | traefik.http.routers.n8n.rule: "Host(`n8n.{{ domain }}`)" 20 | traefik.http.middlewares.n8n-https-redirect.redirectscheme.scheme: "https" 21 | traefik.http.routers.n8n.middlewares: "n8n-https-redirect" 22 | traefik.http.routers.n8n-secure.entrypoints: "https" 23 | traefik.http.routers.n8n-secure.rule: "Host(`n8n.{{ domain }}`)" 24 | traefik.http.routers.n8n-secure.tls: "true" 25 | traefik.http.routers.n8n-secure.service: "n8n" 26 | traefik.http.routers.n8n-secure.middlewares: "authelia@docker" 27 | traefik.http.services.n8n.loadbalancer.server.port: "5678" 28 | traefik.docker.network: "homelab" 29 | -------------------------------------------------------------------------------- /roles/services/tasks/requestrr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create requestrr container 3 | community.docker.docker_container: 4 | name: requestrr 5 | image: darkalfx/requestrr 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | networks: 10 | - name: homelab 11 | volumes: 12 | - "{{ docker_dir }}/requestrr:/root/config" 13 | labels: 14 | traefik.enable: "true" 15 | traefik.http.routers.requestrr.entrypoints: "http" 16 | traefik.http.routers.requestrr.rule: "Host(`requestrr.{{ domain }}`)" 17 | traefik.http.middlewares.requestrr-https-redirect.redirectscheme.scheme: "https" 18 | traefik.http.routers.requestrr.middlewares: "requestrr-https-redirect" 19 | traefik.http.routers.requestrr-secure.entrypoints: "https" 20 | traefik.http.routers.requestrr-secure.rule: "Host(`requestrr.{{ domain }}`)" 21 | traefik.http.routers.requestrr-secure.tls: "true" 22 | traefik.http.routers.requestrr-secure.service: "requestrr" 23 | traefik.http.routers.requestrr-secure.middlewares: "authelia@docker" 24 | traefik.http.services.requestrr.loadbalancer.server.port: "4545" 25 | traefik.docker.network: "homelab" 26 | -------------------------------------------------------------------------------- /roles/services/tasks/uptime_kuma.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create uptime kuma container 3 | community.docker.docker_container: 4 | name: uptime_kuma 5 | image: louislam/uptime-kuma:1 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "{{ docker_dir }}/uptime_kuma/data:/app/data" 11 | security_opts: 12 | - no-new-privileges:true 13 | networks: 14 | - name: homelab 15 | labels: 16 | traefik.enable: "true" 17 | traefik.http.routers.uptime.entrypoints: "http" 18 | traefik.http.routers.uptime.rule: "Host(`uptime.{{ domain }}`)" 19 | traefik.http.middlewares.uptime-https-redirect.redirectscheme.scheme: "https" 20 | traefik.http.routers.uptime.middlewares: "uptime-https-redirect" 21 | traefik.http.routers.uptime-secure.entrypoints: "https" 22 | traefik.http.routers.uptime-secure.rule: "Host(`uptime.{{ domain }}`)" 23 | traefik.http.routers.uptime-secure.tls: "true" 24 | traefik.http.routers.uptime-secure.service: "uptime" 25 | traefik.http.routers.uptime-secure.middlewares: "authelia@docker" 26 | traefik.http.services.uptime.loadbalancer.server.port: "3001" 27 | traefik.docker.network: "homelab" 28 | -------------------------------------------------------------------------------- /roles/services/tasks/vaultwarden.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create vaultwarden container 3 | community.docker.docker_container: 4 | name: vaultwarden 5 | image: vaultwarden/server:latest 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "{{ docker_dir }}/vaultwarden/data:/data" 11 | networks: 12 | - name: homelab 13 | env: 14 | ADMIN_TOKEN: "{{ vaultwarden_admin_argon2id_pass }}" 15 | labels: 16 | traefik.enable: "true" 17 | traefik.http.routers.vault.entrypoints: "http" 18 | traefik.http.routers.vault.rule: "Host(`vault.{{ domain }}`)" 19 | traefik.http.middlewares.vault-https-redirect.redirectscheme.scheme: "https" 20 | traefik.http.routers.vault.middlewares: "vault-https-redirect" 21 | traefik.http.routers.vault-secure.entrypoints: "https" 22 | traefik.http.routers.vault-secure.rule: "Host(`vault.{{ domain }}`)" 23 | traefik.http.routers.vault-secure.tls: "true" 24 | traefik.http.routers.vault-secure.service: "vault" 25 | traefik.http.routers.vault-secure.middlewares: "authelia@docker" 26 | traefik.http.services.vault.loadbalancer.server.port: "80" 27 | traefik.docker.network: "homelab" 28 | -------------------------------------------------------------------------------- /roles/system/tasks/essential.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update and upgrade packages 3 | apt: 4 | update_cache: yes 5 | upgrade: yes 6 | autoremove: yes 7 | 8 | - name: Check if reboot required 9 | stat: 10 | path: /var/run/reboot-required 11 | register: reboot_required_file 12 | 13 | - name: Reboot if required 14 | reboot: 15 | msg: Rebooting due to a kernel update 16 | when: reboot_required_file.stat.exists 17 | 18 | - name: Install packages 19 | package: 20 | name: "{{ packages }}" 21 | state: present 22 | 23 | - name: Install pip packages 24 | ansible.builtin.pip: 25 | name: "{{ pip_packages }}" 26 | state: present 27 | 28 | - name: Free port 53 29 | become: yes 30 | ansible.builtin.lineinfile: 31 | path: /etc/systemd/resolved.conf 32 | regexp: "^#DNS=" 33 | line: "DNS=1.1.1.1" 34 | 35 | - name: Free port 53 Part 2 36 | become: yes 37 | ansible.builtin.lineinfile: 38 | path: /etc/systemd/resolved.conf 39 | regexp: "^#DNSStubListener=yes" 40 | line: "DNSStubListener=no" 41 | 42 | - name: Create symbolic link 43 | shell: sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf 44 | 45 | - name: Reboot the server 46 | ansible.builtin.reboot: 47 | msg: "Rebooting server to free port" 48 | -------------------------------------------------------------------------------- /roles/services/tasks/homarr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create homarr container 3 | community.docker.docker_container: 4 | name: homarr 5 | image: ghcr.io/ajnart/homarr:latest 6 | state: started 7 | restart_policy: unless-stopped 8 | pull: true 9 | networks: 10 | - name: homelab 11 | volumes: 12 | - "{{ docker_dir }}/homarr/configs:/app/data/configs" 13 | - "{{ docker_dir }}/homarr/icons:/app/public/icons" 14 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 15 | labels: 16 | traefik.enable: "true" 17 | traefik.http.routers.homarr.entrypoints: "http" 18 | traefik.http.routers.homarr.rule: "Host(`homarr.{{ domain }}`)" 19 | traefik.http.middlewares.homarr-https-redirect.redirectscheme.scheme: "https" 20 | traefik.http.routers.homarr.middlewares: "homarr-https-redirect" 21 | traefik.http.routers.homarr-secure.entrypoints: "https" 22 | traefik.http.routers.homarr-secure.rule: "Host(`homarr.{{ domain }}`)" 23 | traefik.http.routers.homarr-secure.tls: "true" 24 | traefik.http.routers.homarr-secure.service: "homarr" 25 | traefik.http.routers.homarr-secure.middlewares: "authelia@docker" 26 | traefik.http.services.homarr.loadbalancer.server.port: "7575" 27 | traefik.docker.network: "homelab" 28 | -------------------------------------------------------------------------------- /roles/services/tasks/prowlarr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create prowlarr container 3 | community.docker.docker_container: 4 | name: prowlarr 5 | image: lscr.io/linuxserver/prowlarr 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | env: 10 | PUID: "{{ puid }}" 11 | PGID: "{{ pgid }}" 12 | TZ: "{{ timezone }}" 13 | volumes: 14 | - "{{ docker_dir }}/prowlarr/config:/config" 15 | networks: 16 | - name: homelab 17 | labels: 18 | traefik.enable: "true" 19 | traefik.http.routers.prowlarr.entrypoints: "http" 20 | traefik.http.routers.prowlarr.rule: "Host(`prowlarr.{{ domain }}`)" 21 | traefik.http.middlewares.prowlarr-https-redirect.redirectscheme.scheme: "https" 22 | traefik.http.routers.prowlarr.middlewares: "prowlarr-https-redirect" 23 | traefik.http.routers.prowlarr-secure.entrypoints: "https" 24 | traefik.http.routers.prowlarr-secure.rule: "Host(`prowlarr.{{ domain }}`)" 25 | traefik.http.routers.prowlarr-secure.tls: "true" 26 | traefik.http.routers.prowlarr-secure.service: "prowlarr" 27 | traefik.http.routers.prowlarr-secure.middlewares: "authelia@docker" 28 | traefik.http.services.prowlarr.loadbalancer.server.port: "9696" 29 | traefik.docker.network: "homelab" 30 | -------------------------------------------------------------------------------- /roles/services/tasks/jellyseerr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create jellyseerr container 3 | community.docker.docker_container: 4 | name: jellyseerr 5 | image: fallenbagel/jellyseerr:latest 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "{{ docker_dir }}/jellyseerr/config:/app/config" 11 | networks: 12 | - name: homelab 13 | env: 14 | LOG_LEVEL: debug 15 | TZ: "{{ timezone }}" 16 | labels: 17 | traefik.enable: "true" 18 | traefik.http.routers.jellyseerr.entrypoints: "http" 19 | traefik.http.routers.jellyseerr.rule: "Host(`jellyseerr.{{ domain }}`)" 20 | traefik.http.middlewares.jellyseerr-https-redirect.redirectscheme.scheme: "https" 21 | traefik.http.routers.jellyseerr.middlewares: "jellyseerr-https-redirect" 22 | traefik.http.routers.jellyseerr-secure.entrypoints: "https" 23 | traefik.http.routers.jellyseerr-secure.rule: "Host(`jellyseerr.{{ domain }}`)" 24 | traefik.http.routers.jellyseerr-secure.tls: "true" 25 | traefik.http.routers.jellyseerr-secure.service: "jellyseerr" 26 | traefik.http.routers.jellyseerr-secure.middlewares: "authelia@docker" 27 | traefik.http.services.jellyseerr.loadbalancer.server.port: "5055" 28 | traefik.docker.network: homelab 29 | -------------------------------------------------------------------------------- /roles/services/tasks/homeassistant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create homeassistant container 3 | community.docker.docker_container: 4 | name: homeassistant 5 | image: lscr.io/linuxserver/homeassistant:latest 6 | env: 7 | PUID: "{{ puid }}" 8 | PGID: "{{ pgid }}" 9 | TZ: "{{ timezone }}" 10 | network_mode: host 11 | volumes: 12 | - "{{ docker_dir }}/homeassistant/config:/config" 13 | restart_policy: unless-stopped 14 | labels: 15 | traefik.enable: "true" 16 | traefik.http.routers.homeassistant.entrypoints: "http" 17 | traefik.http.routers.homeassistant.rule: "Host(`homeassistant.{{ domain }}`)" 18 | traefik.http.middlewares.homeassistant-https-redirect.redirectscheme.scheme: "https" 19 | traefik.http.routers.homeassistant.middlewares: "homeassistant-https-redirect" 20 | traefik.http.routers.homeassistant-secure.entrypoints: "https" 21 | traefik.http.routers.homeassistant-secure.rule: "Host(`homeassistant.{{ domain }}`)" 22 | traefik.http.routers.homeassistant-secure.tls: "true" 23 | traefik.http.routers.homeassistant-secure.service: "homeassistant" 24 | traefik.http.routers.homeassistant-secure.middlewares: "authelia@docker" 25 | traefik.http.services.homeassistant.loadbalancer.server.port: "8123" 26 | traefik.docker.network: "homelab" 27 | -------------------------------------------------------------------------------- /roles/services/tasks/radarr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create radarr container 3 | community.docker.docker_container: 4 | name: radarr 5 | image: lscr.io/linuxserver/radarr 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | env: 10 | PUID: "{{ puid }}" 11 | PGID: "{{ pgid }}" 12 | TZ: "{{ timezone }}" 13 | DOCKER_MODS: ghcr.io/gilbn/theme.park:radarr 14 | volumes: 15 | - "{{ docker_dir }}/radarr/config:/config" 16 | - "{{ data_dir }}:/data" 17 | networks: 18 | - name: homelab 19 | labels: 20 | traefik.enable: "true" 21 | traefik.http.routers.radarr.entrypoints: "http" 22 | traefik.http.routers.radarr.rule: "Host(`radarr.{{ domain }}`)" 23 | traefik.http.middlewares.radarr-https-redirect.redirectscheme.scheme: "https" 24 | traefik.http.routers.radarr.middlewares: "radarr-https-redirect" 25 | traefik.http.routers.radarr-secure.entrypoints: "https" 26 | traefik.http.routers.radarr-secure.rule: "Host(`radarr.{{ domain }}`)" 27 | traefik.http.routers.radarr-secure.tls: "true" 28 | traefik.http.routers.radarr-secure.service: "radarr" 29 | traefik.http.routers.radarr-secure.middlewares: "authelia@docker" 30 | traefik.http.services.radarr.loadbalancer.server.port: "7878" 31 | traefik.docker.network: "homelab" 32 | -------------------------------------------------------------------------------- /roles/services/tasks/sonarr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create sonarr container 3 | community.docker.docker_container: 4 | name: sonarr 5 | image: lscr.io/linuxserver/sonarr 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | env: 10 | PUID: "{{ puid }}" 11 | PGID: "{{ pgid }}" 12 | TZ: "{{ timezone }}" 13 | DOCKER_MODS: ghcr.io/gilbn/theme.park:sonarr 14 | volumes: 15 | - "{{ docker_dir }}/sonarr/config:/config" 16 | - "{{ data_dir }}:/data" 17 | networks: 18 | - name: homelab 19 | labels: 20 | traefik.enable: "true" 21 | traefik.http.routers.sonarr.entrypoints: "http" 22 | traefik.http.routers.sonarr.rule: "Host(`sonarr.{{ domain }}`)" 23 | traefik.http.middlewares.sonarr-https-redirect.redirectscheme.scheme: "https" 24 | traefik.http.routers.sonarr.middlewares: "sonarr-https-redirect" 25 | traefik.http.routers.sonarr-secure.entrypoints: "https" 26 | traefik.http.routers.sonarr-secure.rule: "Host(`sonarr.{{ domain }}`)" 27 | traefik.http.routers.sonarr-secure.tls: "true" 28 | traefik.http.routers.sonarr-secure.service: "sonarr" 29 | traefik.http.routers.sonarr-secure.middlewares: "authelia@docker" 30 | traefik.http.services.sonarr.loadbalancer.server.port: "8989" 31 | traefik.docker.network: "homelab" 32 | -------------------------------------------------------------------------------- /roles/services/tasks/portainer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create portainer container 3 | community.docker.docker_container: 4 | name: portainer 5 | image: portainer/portainer-ce 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - /etc/localtime:/etc/localtime:ro 11 | - /var/run/docker.sock:/var/run/docker.sock:ro 12 | - "{{ docker_dir }}/portainer/data:/data" 13 | security_opts: 14 | - no-new-privileges:true 15 | networks: 16 | - name: homelab 17 | labels: 18 | traefik.enable: "true" 19 | traefik.http.routers.portainer.entrypoints: "http" 20 | traefik.http.routers.portainer.rule: "Host(`portainer.{{ domain }}`)" 21 | traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme: "https" 22 | traefik.http.routers.portainer.middlewares: "portainer-https-redirect" 23 | traefik.http.routers.portainer-secure.entrypoints: "https" 24 | traefik.http.routers.portainer-secure.rule: "Host(`portainer.{{ domain }}`)" 25 | traefik.http.routers.portainer-secure.tls: "true" 26 | traefik.http.routers.portainer-secure.service: "portainer" 27 | traefik.http.routers.portainer-secure.middlewares: "authelia@docker" 28 | traefik.http.services.portainer.loadbalancer.server.port: "9000" 29 | traefik.docker.network: "homelab" 30 | -------------------------------------------------------------------------------- /roles/services/tasks/unmanic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create unmanic container 3 | community.docker.docker_container: 4 | name: unmanic 5 | image: josh5/unmanic:latest 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | env: 10 | PUID: "{{ puid }}" 11 | PGID: "{{ pgid }}" 12 | volumes: 13 | - "{{ docker_dir }}/unmanic/config:/config" 14 | - "{{ data_dir }}/media/movies:/library/movies" 15 | - "{{ data_dir }}/media/tv:/library/tv" 16 | - /tmp:/tmp/unmanic 17 | networks: 18 | - name: homelab 19 | labels: 20 | traefik.enable: "true" 21 | traefik.http.routers.unmanic.entrypoints: "http" 22 | traefik.http.routers.unmanic.rule: "Host(`unmanic.{{ domain }}`)" 23 | traefik.http.middlewares.unmanic-https-redirect.redirectscheme.scheme: "https" 24 | traefik.http.routers.unmanic.middlewares: "unmanic-https-redirect" 25 | traefik.http.routers.unmanic-secure.entrypoints: "https" 26 | traefik.http.routers.unmanic-secure.rule: "Host(`unmanic.{{ domain }}`)" 27 | traefik.http.routers.unmanic-secure.tls: "true" 28 | traefik.http.routers.unmanic-secure.service: "unmanic" 29 | traefik.http.routers.unmanic-secure.middlewares: "authelia@docker" 30 | traefik.http.services.unmanic.loadbalancer.server.port: "8888" 31 | traefik.docker.network: "homelab" 32 | -------------------------------------------------------------------------------- /roles/services/tasks/duplicati.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create duplicati container 3 | community.docker.docker_container: 4 | name: duplicati 5 | image: lscr.io/linuxserver/duplicati:latest 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "{{ docker_dir }}/duplicati/config:/config" 11 | - "{{ docker_dir }}/duplicati/backups:/backups" 12 | - "/:/source" 13 | networks: 14 | - name: homelab 15 | env: 16 | PUID: "0" 17 | PGID: "0" 18 | TZ: "{{ timezone }}" 19 | labels: 20 | traefik.enable: "true" 21 | traefik.http.routers.duplicati.entrypoints: "http" 22 | traefik.http.routers.duplicati.rule: "Host(`duplicati.{{ domain }}`)" 23 | traefik.http.middlewares.duplicati-https-redirect.redirectscheme.scheme: "https" 24 | traefik.http.routers.duplicati.middlewares: "duplicati-https-redirect" 25 | traefik.http.routers.duplicati-secure.entrypoints: "https" 26 | traefik.http.routers.duplicati-secure.rule: "Host(`duplicati.{{ domain }}`)" 27 | traefik.http.routers.duplicati-secure.tls: "true" 28 | traefik.http.routers.duplicati-secure.service: "duplicati" 29 | traefik.http.routers.duplicati-secure.middlewares: "authelia@docker" 30 | traefik.http.services.duplicati.loadbalancer.server.port: "8200" 31 | traefik.docker.network: "homelab" 32 | -------------------------------------------------------------------------------- /roles/services/tasks/jellyfin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create jellyfin container 3 | community.docker.docker_container: 4 | name: jellyfin 5 | image: lscr.io/linuxserver/jellyfin 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | env: 10 | PUID: "{{ puid }}" 11 | PGID: "{{ pgid }}" 12 | TZ: "{{ timezone }}" 13 | JELLYFIN_PublishedServerUrl: "{{ ip_address }}" 14 | volumes: 15 | - "{{ docker_dir }}/jellyfin/config:/config" 16 | - "{{ data_dir }}/media:/data/media" 17 | networks: 18 | - name: homelab 19 | labels: 20 | traefik.enable: "true" 21 | traefik.http.routers.jellyfin.entrypoints: "http" 22 | traefik.http.routers.jellyfin.rule: "Host(`jellyfin.{{ domain }}`)" 23 | traefik.http.middlewares.jellyfin-https-redirect.redirectscheme.scheme: "https" 24 | traefik.http.routers.jellyfin.middlewares: "jellyfin-https-redirect" 25 | traefik.http.routers.jellyfin-secure.entrypoints: "https" 26 | traefik.http.routers.jellyfin-secure.rule: "Host(`jellyfin.{{ domain }}`)" 27 | traefik.http.routers.jellyfin-secure.tls: "true" 28 | traefik.http.routers.jellyfin-secure.service: "jellyfin" 29 | traefik.http.routers.jellyfin-secure.middlewares: "authelia@docker" 30 | traefik.http.services.jellyfin.loadbalancer.server.port: "8096" 31 | traefik.docker.network: "homelab" 32 | -------------------------------------------------------------------------------- /roles/services/tasks/syncthing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create syncthing container 3 | community.docker.docker_container: 4 | name: syncthing 5 | image: lscr.io/linuxserver/syncthing:latest 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | ports: 10 | - "22000:22000" 11 | - "22000:22000/udp" 12 | - "21027:21027/udp" 13 | volumes: 14 | - "{{ docker_dir }}/syncthing/config:/config" 15 | - "{{ docker_dir }}/syncthing/:/data1" 16 | networks: 17 | - name: homelab 18 | env: 19 | PUID: "{{ puid }}" 20 | PGID: "{{ pgid }}" 21 | TZ: "{{ timezone }}" 22 | labels: 23 | traefik.enable: "true" 24 | traefik.http.routers.sync.entrypoints: "http" 25 | traefik.http.routers.sync.rule: "Host(`sync.{{ domain }}`)" 26 | traefik.http.middlewares.sync-https-redirect.redirectscheme.scheme: "https" 27 | traefik.http.routers.sync.middlewares: "sync-https-redirect" 28 | traefik.http.routers.sync-secure.entrypoints: "https" 29 | traefik.http.routers.sync-secure.rule: "Host(`sync.{{ domain }}`)" 30 | traefik.http.routers.sync-secure.tls: "true" 31 | traefik.http.routers.sync-secure.service: "sync" 32 | traefik.http.routers.sync-secure.middlewares: "authelia@docker" 33 | traefik.http.services.sync.loadbalancer.server.port: "8384" 34 | traefik.docker.network: "homelab" 35 | -------------------------------------------------------------------------------- /roles/services/tasks/code_server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create codeserver container 3 | community.docker.docker_container: 4 | name: codeserver 5 | image: lscr.io/linuxserver/code-server:latest 6 | pull: true 7 | state: started 8 | restart_policy: unless-stopped 9 | volumes: 10 | - "/home/{{ username }}/:/home/{{ username }}" 11 | - "{{ docker_dir }}/code_server/config:/config" 12 | networks: 13 | - name: homelab 14 | env: 15 | PUID: "{{ puid }}" 16 | PGID: "{{ pgid }}" 17 | PASSWORD: "{{ codeserver_password }}" 18 | SUDO_PASSWORD: "{{ codeserver_password }}" 19 | TZ: "{{ timezone }}" 20 | labels: 21 | traefik.enable: "true" 22 | traefik.http.routers.code.entrypoints: "http" 23 | traefik.http.routers.code.rule: "Host(`code.{{ domain }}`)" 24 | traefik.http.middlewares.code-https-redirect.redirectscheme.scheme: "https" 25 | traefik.http.routers.code.middlewares: "code-https-redirect" 26 | traefik.http.routers.code-secure.entrypoints: "https" 27 | traefik.http.routers.code-secure.rule: "Host(`code.{{ domain }}`)" 28 | traefik.http.routers.code-secure.tls: "true" 29 | traefik.http.routers.code-secure.service: "code" 30 | traefik.http.routers.code-secure.middlewares: "authelia@docker" 31 | traefik.http.services.code.loadbalancer.server.port: "8443" 32 | traefik.docker.network: "homelab" 33 | -------------------------------------------------------------------------------- /roles/services/tasks/wireguard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create wireguard container 3 | community.docker.docker_container: 4 | name: wireguard 5 | image: weejewel/wg-easy 6 | pull: true 7 | sysctls: 8 | "net.ipv4.ip_forward": "1" 9 | "net.ipv4.conf.all.src_valid_mark": "1" 10 | capabilities: 11 | - NET_ADMIN 12 | - SYS_MODULE 13 | state: started 14 | env: 15 | "PASSWORD": "{{ wg_password }}" 16 | "WG_HOST": "{{ ip_address }}" 17 | ports: 18 | - "51820:51820/udp" 19 | volumes: 20 | - "{{ docker_dir }}/wireguard/config:/etc/wireguard" 21 | restart_policy: unless-stopped 22 | networks: 23 | - name: homelab 24 | labels: 25 | traefik.enable: "true" 26 | traefik.http.routers.wg.entrypoints: "http" 27 | traefik.http.routers.wg.rule: "Host(`wg.{{ domain }}`)" 28 | traefik.http.middlewares.wg-https-redirect.redirectscheme.scheme: "https" 29 | traefik.http.routers.wg.middlewares: "wg-https-redirect" 30 | traefik.http.routers.wg-secure.entrypoints: "https" 31 | traefik.http.routers.wg-secure.rule: "Host(`wg.{{ domain }}`)" 32 | traefik.http.routers.wg-secure.tls: "true" 33 | traefik.http.routers.wg-secure.service: "wg" 34 | traefik.http.routers.wg-secure.middlewares: "authelia@docker" 35 | traefik.http.services.wg.loadbalancer.server.port: "51821" 36 | traefik.docker.network: "homelab" 37 | -------------------------------------------------------------------------------- /roles/services/tasks/pihole.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create pihole container 3 | community.docker.docker_container: 4 | name: pihole 5 | image: cbcrowe/pihole-unbound:latest 6 | state: started 7 | restart_policy: unless-stopped 8 | networks_cli_compatible: yes 9 | env: 10 | PUID: "{{ puid }}" 11 | PGID: "{{ pgid }}" 12 | TZ: "{{ timezone }}" 13 | WEBPASSWORD: "{{pihole_password}}" 14 | volumes: 15 | - "{{ docker_dir }}/pihole/config:/config" 16 | - "{{ data_dir }}/pihole:/etc/dnsmasq.d" 17 | networks: 18 | - name: homelab 19 | ports: 20 | - "53:53/tcp" 21 | - "53:53/udp" 22 | - "853:853" 23 | capabilities: 24 | - NET_ADMIN 25 | labels: 26 | traefik.enable: "true" 27 | traefik.http.routers.pihole.entrypoints: "http" 28 | traefik.http.routers.pihole.rule: "Host(`pihole.{{ domain }}`)" 29 | traefik.http.middlewares.pihole-https-redirect.redirectscheme.scheme: "https" 30 | traefik.http.routers.pihole.middlewares: "pihole-https-redirect" 31 | traefik.http.routers.pihole-secure.entrypoints: "https" 32 | traefik.http.routers.pihole-secure.rule: "Host(`pihole.{{ domain }}`)" 33 | traefik.http.routers.pihole-secure.tls: "true" 34 | traefik.http.routers.pihole-secure.service: "pihole" 35 | traefik.http.routers.pihole-secure.middlewares: "authelia@docker" 36 | traefik.http.services.pihole.loadbalancer.server.port: "80" 37 | -------------------------------------------------------------------------------- /tasks/ssh_port_test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: SSH Port Juggle | Try connecting via SSH 3 | wait_for_connection: 4 | timeout: 3 5 | ignore_errors: true 6 | register: _ssh_port_result 7 | 8 | - name: SSH Port Juggle | Set the ansible_port to the fallback default port 9 | set_fact: 10 | ansible_ssh_port: "22" 11 | when: 12 | - _ssh_port_result is failed 13 | 14 | - name: SSH Port Juggle | Try connecting again 15 | wait_for_connection: 16 | timeout: 5 17 | ignore_errors: true 18 | register: _ssh_port_default_result 19 | when: 20 | - _ssh_port_result is failed 21 | 22 | - name: SSH Port Juggle | Try root 23 | set_fact: 24 | ansible_ssh_port: "22" 25 | ansible_ssh_user: "root" 26 | when: 27 | - _ssh_port_result is failed 28 | - _ssh_port_default_result is failed 29 | - _ssh_port_default_cred_result is failed 30 | 31 | - name: Try root 32 | wait_for_connection: 33 | timeout: 5 34 | ignore_errors: true 35 | register: _ssh_port_default_cred_result 36 | when: 37 | - _ssh_port_result is failed 38 | - _ssh_port_default_result is failed 39 | - _ssh_port_default_cred_result is failed 40 | 41 | - name: SSH Port Juggle | Fail 42 | fail: msg="Neither the configured ansible_port {{ ansible_port }} nor the fallback port 22 were reachable" 43 | when: 44 | - _ssh_port_result is failed 45 | - _ssh_port_default_result is defined 46 | - _ssh_port_default_result is failed 47 | - _ssh_port_default_cred_result is defined 48 | - _ssh_port_default_cred_result is failed 49 | - _ssh_port_root_result is defined 50 | - _ssh_port_root_result is failed 51 | -------------------------------------------------------------------------------- /roles/services/tasks/filebrowser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create filebrowser.db file 3 | ansible.builtin.file: 4 | path: "{{ docker_dir }}/filebrowser/filebrowser.db" 5 | state: touch 6 | owner: "{{ username }}" 7 | group: "{{ username }}" 8 | mode: "0755" 9 | access_time: preserve 10 | modification_time: preserve 11 | 12 | - name: Create file browser container 13 | community.docker.docker_container: 14 | name: filebrowser 15 | image: filebrowser/filebrowser 16 | env: 17 | PUID: "{{ puid }}" 18 | PGID: "{{ pgid }}" 19 | pull: true 20 | state: started 21 | networks: 22 | - name: homelab 23 | volumes: 24 | - "/home/{{ username }}/:/srv" 25 | - "{{ docker_dir }}/filebrowser/filebrowser.db:/database.db" 26 | restart_policy: unless-stopped 27 | security_opts: 28 | - no-new-privileges:true 29 | labels: 30 | traefik.enable: "true" 31 | traefik.http.routers.files.entrypoints: "http" 32 | traefik.http.routers.files.rule: "Host(`files.{{ domain }}`)" 33 | traefik.http.middlewares.files-https-redirect.redirectscheme.scheme: "https" 34 | traefik.http.routers.files.middlewares: "files-https-redirect" 35 | traefik.http.routers.files-secure.entrypoints: "https" 36 | traefik.http.routers.files-secure.rule: "Host(`files.{{ domain }}`)" 37 | traefik.http.routers.files-secure.tls: "true" 38 | traefik.http.routers.files-secure.service: "files" 39 | traefik.http.routers.files-secure.middlewares: "authelia@docker" 40 | traefik.http.services.files.loadbalancer.server.port: "80" 41 | traefik.docker.network: "homelab" 42 | -------------------------------------------------------------------------------- /roles/services/tasks/nextcloud.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create nextcloud container 3 | community.docker.docker_container: 4 | name: nextcloud 5 | image: lscr.io/linuxserver/nextcloud:latest 6 | restart_policy: unless-stopped 7 | state: started 8 | networks: 9 | - name: homelab 10 | pull: true 11 | env: 12 | PUID: "{{ puid }}" 13 | PGID: "{{ pgid }}" 14 | TZ: "{{ timezone }}" 15 | volumes: 16 | - "{{ docker_dir }}/nextcloud/config:/config" 17 | - "{{ data_dir }}/nextcloud/data:/data" 18 | labels: 19 | traefik.enable: "true" 20 | traefik.http.routers.nextcloud.entrypoints: "http" 21 | traefik.http.routers.nextcloud.rule: "Host(`nextcloud.{{ domain }}`)" 22 | traefik.http.middlewares.nextcloud-https-redirect.redirectscheme.scheme: "https" 23 | traefik.http.routers.nextcloud.middlewares: "nextcloud-https-redirect" 24 | traefik.http.routers.nextcloud-secure.entrypoints: "https" 25 | traefik.http.routers.nextcloud-secure.rule: "Host(`nextcloud.{{ domain }}`)" 26 | traefik.http.routers.nextcloud-secure.tls: "true" 27 | traefik.http.routers.nextcloud-secure.service: "nextcloud" 28 | traefik.http.routers.nextcloud-secure.middlewares: "authelia@docker" 29 | traefik.http.services.nextcloud.loadbalancer.server.port: "80" 30 | traefik.docker.network: "homelab" 31 | ################ DO THIS AFTER APP CONFIG ################ 32 | # - name: Overwrite protocol to https 33 | # ansible.builtin.lineinfile: 34 | # path: "{{ docker_dir }}/nextcloud/config/www/nextcloud/config/config.php" 35 | # regexp: '^\);' 36 | # line: "'overwriteprotocol' => 'https', );" 37 | -------------------------------------------------------------------------------- /roles/docker/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set amd64 architecture 3 | ansible.builtin.set_fact: 4 | docker_arch: amd64 5 | when: ansible_architecture == "x86_64" 6 | 7 | - name: Set arm64 architecture 8 | ansible.builtin.set_fact: 9 | docker_arch: arm64 10 | when: ansible_architecture == "aarch64" 11 | 12 | - name: Install Docker dependencies 13 | ansible.builtin.package: 14 | name: "{{ docker_dependencies }}" 15 | state: present 16 | 17 | - name: Add Docker GPG key for apt 18 | ansible.builtin.apt_key: 19 | url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg 20 | state: present 21 | when: ansible_distribution in ["Ubuntu", "Debian"] 22 | 23 | - name: Add Docker repository for apt 24 | ansible.builtin.apt_repository: 25 | repo: deb [arch={{ docker_arch }}] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable 26 | state: present 27 | when: ansible_distribution in ["Ubuntu", "Debian"] 28 | 29 | - name: Install docker 30 | ansible.builtin.package: 31 | name: "{{ docker_packages }}" 32 | state: present 33 | 34 | - name: Ensure Docker group exists 35 | ansible.builtin.group: 36 | name: docker 37 | state: present 38 | 39 | - name: Add user to Docker group 40 | ansible.builtin.user: 41 | name: "{{ username }}" 42 | groups: docker 43 | append: true 44 | 45 | - name: Enable Docker service 46 | ansible.builtin.service: 47 | name: docker 48 | enabled: true 49 | state: started 50 | 51 | - name: Create custom docker network 52 | community.docker.docker_network: 53 | name: homelab 54 | state: present 55 | driver: bridge 56 | -------------------------------------------------------------------------------- /roles/system/tasks/user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set the name of a sudo group 3 | set_fact: 4 | sudo_group: sudo 5 | 6 | - name: Ensure the necessary groups exists 7 | group: 8 | name: "{{ item }}" 9 | state: present 10 | loop: 11 | - "{{ username }}" 12 | - docker 13 | 14 | - name: Create a login user 15 | user: 16 | name: "{{ username }}" 17 | password: "{{user_password | password_hash('sha512')}}" 18 | groups: 19 | - "{{ sudo_group }}" 20 | - docker 21 | - users 22 | state: present 23 | append: true 24 | 25 | - name: Chmod the user home directory 26 | file: 27 | path: "/home/{{ username }}" 28 | state: directory 29 | mode: 0755 30 | owner: "{{ username }}" 31 | group: "{{ username }}" 32 | recurse: yes 33 | 34 | - name: Allow '{{ sudo_group }}' group to have passwordless sudo 35 | lineinfile: 36 | path: /etc/sudoers 37 | state: present 38 | regexp: "^%{{ sudo_group }}" 39 | line: "%{{ sudo_group }} ALL=(ALL) NOPASSWD: ALL" 40 | validate: "/usr/sbin/visudo -cf %s" 41 | 42 | - name: Set the default shell 43 | user: 44 | name: "{{ username }}" 45 | shell: "{{ shell }}" 46 | 47 | - name: Disable fish greeting 48 | lineinfile: 49 | path: /home/{{ username }}/.config/fish/fish_variables 50 | state: present 51 | regexp: "fish_greeting:.+" 52 | line: "SETUVAR fish_greeting:" 53 | create: true 54 | owner: "{{ username }}" 55 | group: "{{ username }}" 56 | mode: 0644 57 | when: '"fish" in shell' 58 | 59 | - name: Suppress login messages 60 | file: 61 | name: /home/{{ username }}/.hushlogin 62 | mode: 0644 63 | state: touch 64 | owner: "{{ username }}" 65 | group: "{{ username }}" 66 | modification_time: preserve 67 | access_time: preserve 68 | -------------------------------------------------------------------------------- /roles/services/tasks/transmission.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create transmission container 3 | community.docker.docker_container: 4 | name: transmission 5 | image: haugene/transmission-openvpn 6 | restart_policy: unless-stopped 7 | state: started 8 | networks: 9 | - name: homelab 10 | privileged: yes 11 | devices: 12 | - "/dev/net/tun" 13 | sysctls: 14 | "net.ipv6.conf.all.disable_ipv6": "0" 15 | ports: 16 | - "9091:9091" 17 | - "8888:8888" 18 | volumes: 19 | - "{{ docker_dir }}/transmission/config:/config" 20 | - "{{ data_dir }}/media:/data" 21 | env: 22 | PUID: "{{ puid }}" 23 | PGID: "{{ pgid }}" 24 | TZ: "{{ timezone }}" 25 | OPENVPN_PROVIDER: "{{vpn_provider}}" 26 | OPENVPN_CONFIG: "{{vpn_config}}" 27 | OPENVPN_USERNAME: "{{vpn_username}}" 28 | OPENVPN_PASSWORD: "{{vpn_password}}" 29 | LOCAL_NETWORK: "{{local_network}}" 30 | capabilities: 31 | - NET_ADMIN 32 | labels: 33 | traefik.enable: "true" 34 | traefik.http.routers.transmission.entrypoints: "http" 35 | traefik.http.routers.transmission.rule: "Host(`transmission.{{ domain }}`)" 36 | traefik.http.middlewares.transmission-https-redirect.redirectscheme.scheme: "https" 37 | traefik.http.routers.transmission.middlewares: "transmission-https-redirect" 38 | traefik.http.routers.transmission-secure.entrypoints: "https" 39 | traefik.http.routers.transmission-secure.rule: "Host(`transmission.{{ domain }}`)" 40 | traefik.http.routers.transmission-secure.tls: "true" 41 | traefik.http.routers.transmission-secure.service: "transmission" 42 | traefik.http.routers.transmission-secure.middlewares: "authelia@docker" 43 | traefik.http.services.transmission.loadbalancer.server.port: "9091" 44 | traefik.docker.network: "homelab" 45 | -------------------------------------------------------------------------------- /roles/docker/tasks/dirs.yml: -------------------------------------------------------------------------------- 1 | - name: Create Docker directory 2 | ansible.builtin.file: 3 | path: "{{ docker_dir }}" 4 | state: directory 5 | owner: "{{ username }}" 6 | group: "{{ username }}" 7 | mode: "0755" 8 | 9 | - name: Create data directory 10 | ansible.builtin.file: 11 | path: "{{ item }}" 12 | state: directory 13 | owner: "{{ username }}" 14 | group: "{{ username }}" 15 | mode: "0755" 16 | loop: 17 | - "{{ data_dir }}" 18 | - "{{ data_dir }}/media" 19 | - "{{ data_dir }}/media/movies" 20 | - "{{ data_dir }}/media/tv" 21 | - "{{ data_dir }}/media/torrents" 22 | 23 | - name: Create app directories 24 | ansible.builtin.file: 25 | path: "{{ item }}" 26 | state: directory 27 | owner: "{{ username }}" 28 | group: "{{ username }}" 29 | mode: "0755" 30 | loop: 31 | - "{{ docker_dir }}/authelia" 32 | - "{{ docker_dir }}/code_server" 33 | - "{{ docker_dir }}/dashdot" 34 | - "{{ docker_dir }}/duplicati" 35 | - "{{ docker_dir }}/filebrowser" 36 | - "{{ docker_dir }}/guacamole" 37 | - "{{ docker_dir }}/homarr" 38 | - "{{ docker_dir }}/homeassistant" 39 | - "{{ docker_dir }}/jellyfin" 40 | - "{{ docker_dir }}/jellyseerr" 41 | - "{{ docker_dir }}/monitoring" 42 | - "{{ docker_dir }}/mysql" 43 | - "{{ docker_dir }}/n8n" 44 | - "{{ docker_dir }}/nextcloud" 45 | - "{{ docker_dir }}/pihole" 46 | - "{{ docker_dir }}/portainer" 47 | - "{{ docker_dir }}/prowlarr" 48 | - "{{ docker_dir }}/radarr" 49 | - "{{ docker_dir }}/requestrr" 50 | - "{{ docker_dir }}/sonarr" 51 | - "{{ docker_dir }}/syncthing" 52 | - "{{ docker_dir }}/traefik" 53 | - "{{ docker_dir }}/transmission" 54 | - "{{ docker_dir }}/unmanic" 55 | - "{{ docker_dir }}/uptime_kuma" 56 | - "{{ docker_dir }}/vaultwarden" 57 | - "{{ docker_dir }}/watchtower" 58 | - "{{ docker_dir }}/wireguard" 59 | -------------------------------------------------------------------------------- /roles/services/tasks/finalcheck.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add required permissions to data and docker_apps directories 3 | ansible.builtin.file: 4 | path: "{{ item }}" 5 | recurse: true 6 | state: directory 7 | owner: "{{ username }}" 8 | group: "{{ username }}" 9 | mode: "0755" 10 | loop: 11 | - "{{ data_dir }}" 12 | - "{{ docker_dir }}/authelia" 13 | - "{{ docker_dir }}/bazarr" 14 | - "{{ docker_dir }}/code_server" 15 | - "{{ docker_dir }}/dashdot" 16 | - "{{ docker_dir }}/deluge" 17 | - "{{ docker_dir }}/duplicati" 18 | - "{{ docker_dir }}/filebrowser" 19 | - "{{ docker_dir }}/guacamole" 20 | - "{{ docker_dir }}/heimdall" 21 | - "{{ docker_dir }}/homarr" 22 | - "{{ docker_dir }}/jellyfin" 23 | - "{{ docker_dir }}/jellyseerr" 24 | - "{{ docker_dir }}/monitoring" 25 | - "{{ docker_dir }}/mysql" 26 | - "{{ docker_dir }}/n8n" 27 | - "{{ docker_dir }}/nextcloud" 28 | - "{{ docker_dir }}/portainer" 29 | - "{{ docker_dir }}/prowlarr" 30 | - "{{ docker_dir }}/qbittorrent" 31 | - "{{ docker_dir }}/radarr" 32 | - "{{ docker_dir }}/requestrr" 33 | - "{{ docker_dir }}/sonarr" 34 | - "{{ docker_dir }}/syncthing" 35 | - "{{ docker_dir }}/transmission" 36 | - "{{ docker_dir }}/traefik" 37 | - "{{ docker_dir }}/unmanic" 38 | - "{{ docker_dir }}/uptime_kuma" 39 | - "{{ docker_dir }}/vaultwarden" 40 | - "{{ docker_dir }}/watchtower" 41 | - "{{ docker_dir }}/wireguard" 42 | 43 | - name: Reboot the server 44 | ansible.builtin.reboot: 45 | msg: "Rebooting server to finish setup" 46 | 47 | - name: Check if traefik is working properly with authelia middleware 48 | ansible.builtin.uri: 49 | url: "https://wg.{{ domain }}" 50 | ignore_errors: true 51 | register: traefik_authelia_check 52 | 53 | - name: Wait to ensure authelia is up and running 54 | ansible.builtin.wait_for: 55 | timeout: 10 56 | when: traefik_authelia_check.status != 200 57 | 58 | - name: Restart traefik container to ensure it can find authelia middleware 59 | community.docker.docker_container: 60 | name: traefik 61 | state: started 62 | restart: true 63 | timeout: 300 64 | when: traefik_authelia_check.status != 200 65 | -------------------------------------------------------------------------------- /roles/services/tasks/docker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set amd64 architecture 3 | ansible.builtin.set_fact: 4 | docker_arch: amd64 5 | when: ansible_architecture == "x86_64" 6 | 7 | - name: Set arm64 architecture 8 | ansible.builtin.set_fact: 9 | docker_arch: arm64 10 | when: ansible_architecture == "aarch64" 11 | 12 | - name: Set RedHat distribution 13 | ansible.builtin.set_fact: 14 | rh_distribution: "{{ 'rhel' if ansible_distribution == 'RedHat' else ansible_distribution | lower }}" 15 | 16 | - name: Install Docker dependencies 17 | ansible.builtin.package: 18 | name: "{{ docker_dependencies }}" 19 | state: present 20 | 21 | - name: Add Docker GPG key for apt 22 | ansible.builtin.apt_key: 23 | url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg 24 | state: present 25 | when: ansible_distribution in ["Ubuntu", "Debian"] 26 | 27 | - name: Add Docker repository for apt 28 | ansible.builtin.apt_repository: 29 | repo: deb [arch={{ docker_arch }}] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable 30 | state: present 31 | when: ansible_distribution in ["Ubuntu", "Debian"] 32 | 33 | - name: Add signing key for yum 34 | ansible.builtin.rpm_key: 35 | key: "https://download.docker.com/linux/{{ rh_distribution }}/gpg" 36 | state: present 37 | when: ansible_distribution in ["Fedora", "CentOS", "RedHat"] 38 | 39 | - name: Add repository into repo.d list for yum 40 | ansible.builtin.yum_repository: 41 | name: docker 42 | description: docker repository 43 | baseurl: "https://download.docker.com/linux/{{ rh_distribution }}/$releasever/$basearch/stable" 44 | enabled: true 45 | gpgcheck: true 46 | gpgkey: "https://download.docker.com/linux/{{ rh_distribution }}/gpg" 47 | when: ansible_distribution in ["Fedora", "CentOS", "RedHat"] 48 | 49 | - name: Install docker 50 | ansible.builtin.package: 51 | name: "{{ docker_packages }}" 52 | state: present 53 | 54 | - name: Ensure Docker group exists 55 | ansible.builtin.group: 56 | name: docker 57 | state: present 58 | 59 | - name: Add user to Docker group 60 | ansible.builtin.user: 61 | name: "{{ username }}" 62 | groups: docker 63 | append: true 64 | 65 | - name: Enable Docker service 66 | ansible.builtin.service: 67 | name: docker 68 | enabled: true 69 | state: started 70 | 71 | - name: Create custom docker network 72 | community.docker.docker_network: 73 | name: homelab 74 | state: present 75 | driver: bridge 76 | -------------------------------------------------------------------------------- /group_vars/all/vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | #Please see variablehelp.md if you are confused 3 | 4 | username: "" 5 | 6 | puid: "" 7 | 8 | pgid: "" 9 | 10 | ip_address: "" 11 | 12 | timezone: "" 13 | 14 | domain: "" 15 | 16 | cloudflare_email: "" 17 | 18 | cloudflare_api_key: "" 19 | 20 | traefik_basic_auth_hash: "" 21 | 22 | jwt_secret: "" 23 | 24 | authelia_sqlite_encryption_key: "" 25 | 26 | google_mail: "" 27 | 28 | google_insecure_app_pass: "" 29 | 30 | authelia_admin_mail: "" 31 | 32 | authelia_admin_argon2id_pass: "" 33 | 34 | vaultwarden_admin_argon2id_pass: "" 35 | 36 | wg_password: "" 37 | 38 | codeserver_password: "" 39 | 40 | pihole_password: "" 41 | 42 | mysql_password: "" 43 | 44 | vpn_provider: "" 45 | 46 | vpn_config: "" 47 | 48 | vpn_username: "" 49 | 50 | vpn_password: "" 51 | 52 | local_network: "" 53 | 54 | shell: /usr/bin/fish 55 | 56 | docker_dir: /home/{{ username }}/docker 57 | 58 | data_dir: /home/{{ username }}/data 59 | 60 | packages: 61 | - unzip 62 | - wget 63 | - curl 64 | - git 65 | - python3 66 | - python3-pip 67 | - fish 68 | - speedtest-cli 69 | - htop 70 | - exa 71 | - git 72 | - neofetch 73 | - neovim 74 | - tmux 75 | - expect 76 | 77 | pip_packages: 78 | - ansible 79 | - github3.py 80 | - docker 81 | 82 | docker_dependencies: 83 | - ca-certificates 84 | - gnupg 85 | - curl 86 | 87 | docker_packages: 88 | - docker-ce 89 | - docker-ce-cli 90 | - containerd.io 91 | - docker-buildx-plugin 92 | - docker-compose-plugin 93 | 94 | # 95 | # SSH (geerlingguy.security) 96 | # 97 | security_ssh_port: 69 #ssh listening port (default 22) 98 | security_sudoers_passwordless: ["{{ username }}"] 99 | security_autoupdate_reboot: true 100 | security_autoupdate_reboot_time: "23:00" 101 | security_autoupdate_mail_on_error: false 102 | security_ssh_password_authentication: "no" 103 | security_ssh_permit_root_login: "no" 104 | security_ssh_usedns: "no" 105 | security_ssh_permit_empty_password: "no" 106 | security_ssh_challenge_response_auth: "no" 107 | security_ssh_gss_api_authentication: "no" 108 | security_ssh_x11_forwarding: "no" 109 | -------------------------------------------------------------------------------- /variablehelp.md: -------------------------------------------------------------------------------- 1 | # Variable Help 2 | 3 | This document includes help for setting up your variables in the `group_vars/all/vars.yml` file. 4 | Remove <> tags but keep your variables inside of the "". Please be mindful that some passwords and API keys are in **plain text**. 5 | 6 | `username`: Your Ubuntu username you chose at setup. 7 | 8 | `puid`: Run the `id` command on your server and use the value in the `uid` field. 9 | 10 | `pgid`: Run the `id` command on your server and use the value in the `gid` field. 11 | 12 | `ip_address`: Your server IP address. Run the `ip a` on your server to see your IP. 13 | 14 | `timezone`: Your timezone. Run the `timedatectl list-timezones` command to see what is a valid timezone. 15 | 16 | `domain`: Your domain name 17 | 18 | `cloudflare_email`: The email you used to sign up for Cloudflare 19 | 20 | `cloudflare_api_key`: Your [Cloudflare global API key](https://developers.cloudflare.com/fundamentals/api/get-started/keys/) 21 | 22 | `traefik_basic_auth_hash`: Generate your hashed login credentials with the following command: 23 | 24 | ```bash 25 | echo $(htpasswd -nb "" "") | sed -e s/\\$/\\$\\$/g 26 | ``` 27 | 28 | Replace `` with your username and `` with your password to be hashed. 29 | 30 | `jwt_secret`: A random alphanumeric string. Generate it with this command: 31 | 32 | ```bash 33 | docker run authelia/authelia:latest authelia crypto rand --length 64 --charset alphanumeric 34 | ``` 35 | 36 | `authelia_sqlite_encryption_key`: A random alphanumeric string with at least 20 characters. Run the command above to generate one. **Don't** reuse keys. 37 | 38 | `google_mail`: Your google email 39 | 40 | `google_insecure_app_pass`: An app password that allows SMTP through google. See [instructions](https://support.google.com/accounts/answer/185833?hl=en) on how to get yours. Make sure it's the same account that's used above. 41 | 42 | `authelia_admin_mail`: The admin email used in Authelia 43 | 44 | `authelia_admin_argon2id_pass`: A hashed password using the [Argon2 encryption algorithm](https://www.authelia.com/reference/guides/passwords/). 45 | 46 | ```bash 47 | docker run authelia/authelia:latest authelia crypto hash generate argon2 --password '' 48 | ``` 49 | 50 | `vaultwarden_admin_argon2id_pass`: Use the command above to generate a hashed password. **Don't** reuse passwords. 51 | 52 | `wg_password`: The password to login into your wireguard client. 53 | 54 | `codeserver_password`: The password to login into your code server client. 55 | 56 | `pihole_password`: The password to login into your pihole client. 57 | 58 | `mysql_password`: The password to login into your mysql client. 59 | 60 | `vpn_provider`: The VPN provider you have an account with. See [supported VPN Clients](https://haugene.github.io/docker-transmission-openvpn/supported-providers/) 61 | 62 | `vpn_config`: The VPN server you want your Transmission client to connect to. Example: `nl_all` 63 | 64 | `vpn_username`: The username you used to sign up to a VPN service. 65 | 66 | `vpn_password`: The password you used to sign up to a VPN service. 67 | 68 | `local_network`: Your local network. See [how to find it](https://www.youtube.com/watch?v=d2pnH4k9rZ0) Example: `192.168.0.0/16` 69 | -------------------------------------------------------------------------------- /roles/services/tasks/traefik.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create traefik data directory 3 | ansible.builtin.file: 4 | path: "{{ docker_dir }}/traefik/data" 5 | state: directory 6 | mode: "0755" 7 | access_time: preserve 8 | modification_time: preserve 9 | 10 | - name: Create acme.json file 11 | ansible.builtin.file: 12 | path: "{{ docker_dir }}/traefik/data/acme.json" 13 | state: touch 14 | mode: "0600" 15 | access_time: preserve 16 | modification_time: preserve 17 | 18 | - name: Create traefik.yml config file 19 | ansible.builtin.copy: 20 | content: | 21 | api: 22 | debug: true 23 | dashboard: true 24 | insecure: true 25 | entryPoints: 26 | http: 27 | address: ":80" 28 | http: 29 | redirections: 30 | entryPoint: 31 | to: "https" 32 | scheme: "https" 33 | https: 34 | address: ":443" 35 | serversTransport: 36 | insecureSkipVerify: true 37 | providers: 38 | docker: 39 | endpoint: "unix:///var/run/docker.sock" 40 | exposedByDefault: false 41 | certificatesResolvers: 42 | cloudflare: 43 | acme: 44 | email: "{{ cloudflare_email }}" 45 | storage: "acme.json" 46 | dnsChallenge: 47 | provider: "cloudflare" 48 | resolvers: 49 | - "1.1.1.1:53" 50 | - "1.0.0.1:53" 51 | dest: "{{ docker_dir }}/traefik/data/traefik.yml" 52 | mode: "0644" 53 | owner: "{{ username }}" 54 | group: "{{ username }}" 55 | 56 | - name: Deploy traefik with docker container 57 | community.docker.docker_container: 58 | name: traefik 59 | image: traefik:latest 60 | restart_policy: unless-stopped 61 | state: started 62 | security_opts: 63 | - no-new-privileges:true 64 | networks: 65 | - name: homelab 66 | ports: 67 | - "80:80" 68 | - "443:443" 69 | 70 | volumes: 71 | - /var/run/docker.sock:/var/run/docker.sock:ro 72 | - "{{ docker_dir }}/traefik/data/acme.json:/acme.json" 73 | - "{{ docker_dir }}/traefik/data/traefik.yml:/traefik.yml:ro" 74 | env: 75 | CF_API_EMAIL: "{{ cloudflare_email }}" 76 | CF_API_KEY: "{{ cloudflare_api_key }}" 77 | labels: 78 | traefik.enable: "true" 79 | traefik.http.routers.traefik.entrypoints: "http" 80 | traefik.http.routers.traefik.rule: "Host(`traefik.{{ domain }}`)" 81 | traefik.http.middlewares.traefik-auth.basicauth.users: "{{ traefik_basic_auth_hash }}" 82 | traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme: "https" 83 | traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto: "https" 84 | traefik.http.routers.traefik.middlewares: "traefik-https-redirect" 85 | traefik.http.routers.traefik-secure.entrypoints: "https" 86 | traefik.http.routers.traefik-secure.rule: "Host(`traefik.{{ domain }}`)" 87 | traefik.http.routers.traefik-secure.middlewares: "traefik-auth" 88 | traefik.http.routers.traefik-secure.tls: "true" 89 | traefik.http.routers.traefik-secure.tls.certresolver: "cloudflare" 90 | traefik.http.routers.traefik-secure.tls.domains[0].main: "{{ domain }}" 91 | traefik.http.routers.traefik-secure.tls.domains[0].sans: "*.{{ domain }}" 92 | traefik.http.routers.traefik-secure.service: "api@internal" 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Home Server Ansible Playbooks 2 | 3 | ## Ansible Playbooks to help you set up your own server at home 4 | 5 | These playbooks will configure and update Ubuntu, install Docker, and deploy the containers. 6 | It uses Traefik as it's reverse proxy manager and Authelia for Two Factor Authentication. See the services list [here](serviceslist.md). All of the services will be accessible at https://\.\ 7 | 8 | **Important**: Make sure you set SSL/TLS encryption mode to **full** in Cloudflare. 9 | 10 | ## Acknowledgments 11 | 12 | This project is based heavily on [Rishav Nandi's Ansible Homelab.](https://github.com/rishavnandi/ansible_homelab) 13 | 14 | ## Prerequisites 15 | 16 | In order for you to use these playbooks, you'll need a couple things: 17 | 18 | - Basic knowledge of internet protocols (SSH, TCP/UDP etc.), Ansible and Linux 19 | - Router with ports 80 and 443 forwarded to your servers IP address 20 | - Google Account 21 | - Domain name 22 | - Cloudflare account with your domain name [nameservers](https://www.youtube.com/watch?v=uqlo3lCqiy0) pointed to their DNS Servers 23 | - A [supported VPN](https://haugene.github.io/docker-transmission-openvpn/supported-providers/) to use with Transmission* 24 | - A fresh install of Ubuntu Server LTS 22.04 25 | 26 | *If you don't plan on using Transmission then the VPN is not needed. 27 | 28 | ## Setup 29 | 30 | Once you've installed Ubuntu, you'll need an SSH Key for Ansible to use. You will need to create an one and copy it to the server. This can be done with the following commands: 31 | 32 | ```bash 33 | ssh-keygen -o -a 100 -t ed25519 -f -C 34 | ssh-copy-id -i ~/.ssh/homeserver @ 35 | ``` 36 | 37 | Note: I'd recommend storing the ssh file at `~/.ssh/homeserver` 38 | 39 | Fork this repository, then clone it to your local machine and run the following command to install the required roles: 40 | 41 | ```bash 42 | git clone https://github.com/nickjg1/homeserver-ansible 43 | ansible-galaxy install -r requirements.yml 44 | ``` 45 | 46 | Change directories and create an ansible vault file with the following command and enter a password when prompted: 47 | 48 | ```bash 49 | cd homeserver-ansible 50 | ansible-vault create group_vars/all/vault.yml 51 | ``` 52 | 53 | Open the vault file with the following command: 54 | 55 | ```bash 56 | ansible-vault edit group_vars/all/vault.yml 57 | ``` 58 | 59 | Paste the following into the vault file and replace the values with your own: 60 | 61 | ```yaml 62 | user_password: "" 63 | ``` 64 | 65 | ## Configuration 66 | 67 | Make all the necessary changes to the `group_vars/all/vars.yml` and `hosts/hosts` files to match your environment. Extra packages can be added to `group_vars/all/vars.yml`. Any unwanted services can be removed in the `services/tasks/main.yml` file. See [variable help](variablehelp.md) for more information. 68 | 69 | ## Notes and Disclaimers 70 | 71 | This playbook opens your server up to the internet and potentially malicious attacks. Two factor authentication, Cloudflare and [Jeff Geerling's Security Role](https://github.com/geerlingguy/ansible-role-security) offer good layers of protection, but it's always good practice to be mindful of the risks. Further configuration in Cloudflare can strengthen your security. 72 | 73 | This also changes the default listening port of SSH to 69. It can be changed in `group_vars/all/vars.yml`. 74 | 75 | ## Installation 76 | 77 | Run this command, enter your sudo password and vault password when prompted: 78 | 79 | ```bash 80 | ansible-playbook run.yml -K --ask-vault-pass 81 | ``` 82 | 83 | ## Post Installation and Troubleshooting 84 | 85 | If you need help setting services up or have any issues with your installation, see [post installation help](postinstallation.md). 86 | 87 | ## Credit 88 | 89 | - [Rishav Nandi](https://github.com/rishavnandi) 90 | - [Jeff Geerling](https://github.com/geerlingguy) 91 | -------------------------------------------------------------------------------- /roles/services/tasks/monitoring.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create prometheus directory 3 | ansible.builtin.file: 4 | path: "/etc/prometheus/" 5 | state: directory 6 | owner: "{{ username }}" 7 | group: "{{ username }}" 8 | mode: "0755" 9 | 10 | - name: Create prometheus.yml file 11 | ansible.builtin.copy: 12 | content: | 13 | global: 14 | scrape_interval: 15s 15 | scrape_configs: 16 | - job_name: 'prometheus' 17 | scrape_interval: 5s 18 | static_configs: 19 | - targets: ['localhost:9090'] 20 | - job_name: 'node_exporter' 21 | static_configs: 22 | - targets: ['node_exporter:9100'] 23 | - job_name: 'cadvisor' 24 | static_configs: 25 | - targets: ['cadvisor:8080'] 26 | dest: /etc/prometheus/prometheus.yml 27 | owner: "{{ username }}" 28 | group: "{{ username }}" 29 | mode: "0755" 30 | 31 | - name: Create volumes for monitoring containers 32 | community.docker.docker_volume: 33 | name: "{{ item }}" 34 | state: present 35 | driver: local 36 | loop: 37 | - prometheus-data 38 | - grafana-data 39 | 40 | - name: Create prometheus container 41 | community.docker.docker_container: 42 | name: prometheus 43 | image: prom/prometheus:latest 44 | pull: true 45 | state: started 46 | volumes: 47 | - /etc/prometheus/:/etc/prometheus/ 48 | - prometheus-data:/prometheus 49 | restart_policy: unless-stopped 50 | command: "--config.file=/etc/prometheus/prometheus.yml" 51 | networks: 52 | - name: homelab 53 | 54 | - name: Create node exporter container 55 | community.docker.docker_container: 56 | name: node_exporter 57 | image: quay.io/prometheus/node-exporter:latest 58 | pull: true 59 | state: started 60 | pid_mode: host 61 | restart_policy: unless-stopped 62 | command: "--path.rootfs=/host" 63 | volumes: 64 | - /:/host:ro,rslave 65 | networks: 66 | - name: homelab 67 | 68 | - name: Get cadvisor latest release 69 | community.general.github_release: 70 | user: google 71 | repo: cadvisor 72 | action: latest_release 73 | register: cadvisor_latest_release 74 | 75 | - name: Create cadvisor container 76 | community.docker.docker_container: 77 | name: cadvisor 78 | image: gcr.io/cadvisor/cadvisor:{{ cadvisor_latest_release.tag }} 79 | pull: true 80 | state: started 81 | restart_policy: unless-stopped 82 | privileged: true 83 | volumes: 84 | - /:/rootfs:ro 85 | - /var/run:/var/run:ro 86 | - /sys:/sys:ro 87 | - /var/lib/docker/:/var/lib/docker:ro 88 | - /dev/disk/:/dev/disk:ro 89 | devices: 90 | - /dev/kmsg 91 | networks: 92 | - name: homelab 93 | 94 | - name: Create grafana container 95 | community.docker.docker_container: 96 | name: grafana 97 | image: grafana/grafana-oss:latest 98 | pull: true 99 | state: started 100 | restart_policy: unless-stopped 101 | volumes: 102 | - grafana-data:/var/lib/grafana 103 | networks: 104 | - name: homelab 105 | labels: 106 | traefik.enable: "true" 107 | traefik.http.routers.grafana.entrypoints: "http" 108 | traefik.http.routers.grafana.rule: "Host(`grafana.{{ domain }}`)" 109 | traefik.http.middlewares.grafana-https-redirect.redirectscheme.scheme: "https" 110 | traefik.http.routers.grafana.middlewares: "grafana-https-redirect" 111 | traefik.http.routers.grafana-secure.entrypoints: "https" 112 | traefik.http.routers.grafana-secure.rule: "Host(`grafana.{{ domain }}`)" 113 | traefik.http.routers.grafana-secure.tls: "true" 114 | traefik.http.routers.grafana-secure.service: "grafana" 115 | traefik.http.routers.grafana-secure.middlewares: "authelia@docker" 116 | traefik.http.services.grafana.loadbalancer.server.port: "3000" 117 | traefik.docker.network: "homelab" 118 | -------------------------------------------------------------------------------- /roles/services/tasks/authelia.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create authelia config directory 3 | ansible.builtin.file: 4 | path: "{{ docker_dir }}/authelia/config" 5 | state: directory 6 | mode: "0755" 7 | access_time: preserve 8 | modification_time: preserve 9 | 10 | - name: Create authelia config file 11 | ansible.builtin.copy: 12 | content: | 13 | --- 14 | server: 15 | host: 0.0.0.0 16 | port: 9091 17 | log: 18 | level: debug 19 | theme: dark 20 | # This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE 21 | jwt_secret: "{{ jwt_secret }}" 22 | default_redirection_url: "https://auth.{{ domain }}" 23 | totp: 24 | issuer: authelia.com 25 | 26 | authentication_backend: 27 | file: 28 | path: /config/users_database.yml 29 | password: 30 | algorithm: argon2id 31 | iterations: 1 32 | salt_length: 16 33 | parallelism: 8 34 | memory: 64 35 | 36 | access_control: 37 | default_policy: deny 38 | rules: 39 | # Rules applied to everyone 40 | - domain: "code.{{ domain }}" 41 | policy: two_factor 42 | - domain: "dash.{{ domain }}" 43 | policy: bypass 44 | - domain: "duplicati.{{ domain }}" 45 | policy: two_factor 46 | - domain: "files.{{ domain }}" 47 | policy: two_factor 48 | - domain: "guac.{{ domain }}" 49 | policy: two_factor 50 | - domain: "homarr.{{ domain }}" 51 | policy: two_factor 52 | - domain: "homeassistant.{{ domain }}" 53 | policy: two_factor 54 | - domain: "jellyfin.{{ domain }}" 55 | policy: two_factor 56 | - domain: "jellyseerr.{{ domain }}" 57 | policy: two_factor 58 | - domain: "grafana.{{ domain }}" 59 | policy: two_factor 60 | - domain: "n8n.{{ domain }}" 61 | policy: two_factor 62 | - domain: "nextcloud.{{ domain }}" 63 | policy: bypass 64 | - domain: "portainer.{{ domain }}" 65 | policy: two_factor 66 | - domain: "prowlarr.{{ domain }}" 67 | policy: two_factor 68 | - domain: "radarr.{{ domain }}" 69 | policy: two_factor 70 | - domain: "requestrr.{{ domain }}" 71 | policy: two_factor 72 | - domain: "sonarr.{{ domain }}" 73 | policy: two_factor 74 | - domain: "sync.{{ domain }}" 75 | policy: two_factor 76 | - domain: "traefik.{{ domain }}" 77 | policy: two_factor 78 | - domain: "unmanic.{{ domain }}" 79 | policy: two_factor 80 | - domain: "uptime.{{ domain }}" 81 | policy: two_factor 82 | - domain: "vault.{{ domain }}" 83 | policy: bypass 84 | - domain: "wg.{{ domain }}" 85 | policy: two_factor 86 | - domain: "bazarr.{{ domain }}" 87 | policy: two_factor 88 | - domain: "transmission.{{ domain }}" 89 | policy: two_factor 90 | - domain: "pihole.{{ domain }}" 91 | policy: two_factor 92 | 93 | 94 | session: 95 | name: authelia_session 96 | # This secret can also be set using the env variables AUTHELIA_SESSION_SECRET_FILE 97 | secret: unsecure_session_secret 98 | expiration: 10800 # 3 hours 99 | inactivity: 300 # 5 minutes 100 | domain: "{{ domain }}" # Should match whatever your root protected domain is 101 | 102 | regulation: 103 | max_retries: 3 104 | find_time: 120 105 | ban_time: 300 106 | 107 | storage: 108 | encryption_key: "{{ authelia_sqlite_encryption_key }}" 109 | local: 110 | path: /config/db.sqlite3 111 | 112 | notifier: 113 | smtp: 114 | username: "{{ google_mail }}" 115 | # Password can also be set using a secret: https://www.authelia.com/configuration/methods/secrets/ 116 | password: "{{ google_insecure_app_pass }}" 117 | sender: "authelia@{{ domain }}" 118 | host: smtp.gmail.com 119 | port: 587 120 | ... 121 | dest: "{{ docker_dir }}/authelia/config/configuration.yml" 122 | mode: "0644" 123 | owner: "{{ username }}" 124 | group: "{{ username }}" 125 | changed_when: false 126 | 127 | - name: Create authelia users database file 128 | ansible.builtin.copy: 129 | content: | 130 | --- 131 | users: 132 | admin: 133 | displayname: admin 134 | email: "{{ authelia_admin_mail }}" 135 | password: "{{ authelia_admin_argon2id_pass }}" 136 | groups: 137 | - admins 138 | - dev 139 | ... 140 | dest: "{{ docker_dir }}/authelia/config/users_database.yml" 141 | mode: "0644" 142 | owner: "{{ username }}" 143 | group: "{{ username }}" 144 | changed_when: false 145 | 146 | - name: Create authelia container 147 | community.docker.docker_container: 148 | name: authelia 149 | image: authelia/authelia:latest 150 | state: started 151 | restart_policy: unless-stopped 152 | volumes: 153 | - "{{ docker_dir }}/authelia/config:/config" 154 | networks: 155 | - name: homelab 156 | labels: 157 | traefik.enable: "true" 158 | traefik.http.routers.authelia.rule: "Host(`auth.{{ domain }}`)" 159 | traefik.http.routers.authelia.entrypoints: "https" 160 | traefik.http.routers.authelia.tls: "true" 161 | traefik.http.middlewares.authelia.forwardauth.address: "http://authelia:9091/api/verify?rd=https://auth.{{ domain }}" 162 | traefik.http.middlewares.authelia.forwardauth.trustForwardHeader: "true" 163 | traefik.http.middlewares.authelia.forwardauth.authResponseHeaders: "Remote-User, Remote-Groups, Remote-Name, Remote-Email" 164 | exposed_ports: 165 | - "9091" 166 | env: 167 | TZ: "{{ timezone }}" 168 | --------------------------------------------------------------------------------