--- - name: dirs file: state=directory path="{{ item }}" # mode= o= g= loop: - "{{ headscale_dir }}" - "{{ headscale_dir }}/config" - "{{ headscale_dir }}/data" - name: config file template: src="config.yaml.j2" dest="{{ headscale_dir }}/config/config.yaml" # mode= o= g= # create an empty database file if it doesn't exist - name: database file copy: force="no" content="" dest="{{ headscale_dir }}/data/db.sqlite" # mode= o= g= - name: headscale coordination server docker_container: name: headscale image: headscale/headscale:latest-alpine pull: "{{ headscale_pull }}" command: headscale serve restart_policy: unless-stopped volumes: - "{{ headscale_dir }}/config:/etc/headscale:ro" - "{{ headscale_dir }}/data:/var/lib/headscale" labels: traefik.enable: "true" traefik.http.routers.headscale.entryPoints: "web_https" traefik.http.routers.headscale.rule: "Host(`{{ headscale_domain }}`)" traefik.http.routers.headscale.middlewares: "chain-authelia@file" published_ports: ### local testing - "9441:8080" - "9442:9090" # /metrics - "50443:50443" # /headscale grpc API exposed_ports: - "50443" # (If a given namespace already exists, the command issues an error # message but the exit code is 0 (successful).) - name: namespaces aka tailnets community.docker.docker_container_exec: container: headscale argv: ['headscale', 'namespaces', 'create', "{{ item }}"] register: result changed_when: "'Namespace created' in result.stdout_lines" loop: "{{ headscale_namespaces }}" # https://github.com/gurucomputing/headscale-ui - name: headscale UI docker_container: name: headscale-ui image: ghcr.io/gurucomputing/headscale-ui:latest pull: "{{ headscale_pull }}" restart_policy: unless-stopped labels: traefik.enable: "true" traefik.http.routers.headscale.entryPoints: "web_https" traefik.http.routers.headscale.rule: "Host(`{{ headscale_domain }}`) && PathPrefix(`/web`)" traefik.http.routers.headscale.middlewares: "chain-authelia@file" exposed_ports: - "80"