{"id":50488845,"url":"https://github.com/anqorithm/headscale-k0s-vpn","last_synced_at":"2026-06-02T00:31:21.301Z","repository":{"id":349173884,"uuid":"1174790050","full_name":"anqorithm/headscale-k0s-vpn","owner":"anqorithm","description":"A local demo that runs a Kubernetes cluster behind a self-hosted VPN. Your apps are only accessible through the encrypted VPN mesh invisible to the outside world.","archived":false,"fork":false,"pushed_at":"2026-03-06T21:18:40.000Z","size":1265,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T17:29:42.994Z","etag":null,"topics":["headscale","k0s","k8s","security","vpn","wireguard"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anqorithm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-06T21:02:52.000Z","updated_at":"2026-03-23T10:16:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/anqorithm/headscale-k0s-vpn","commit_stats":null,"previous_names":["anqorithm/headscale-k0s-vpn"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/anqorithm/headscale-k0s-vpn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anqorithm%2Fheadscale-k0s-vpn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anqorithm%2Fheadscale-k0s-vpn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anqorithm%2Fheadscale-k0s-vpn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anqorithm%2Fheadscale-k0s-vpn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anqorithm","download_url":"https://codeload.github.com/anqorithm/headscale-k0s-vpn/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anqorithm%2Fheadscale-k0s-vpn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33800676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["headscale","k0s","k8s","security","vpn","wireguard"],"created_at":"2026-06-02T00:31:20.565Z","updated_at":"2026-06-02T00:31:21.293Z","avatar_url":"https://github.com/anqorithm.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Headscale + k0s VPN Cluster\n\nA local demo that runs a Kubernetes cluster behind a self-hosted VPN.\n\nYour apps are only accessible through the encrypted VPN mesh invisible to the outside world.\n\n## Table of Contents\n\n- [TLDR](#tldr)\n- [Requirements](#requirements)\n- [Versions](#versions)\n- [What's inside](#whats-inside)\n- [Architecture](#architecture)\n- [How it works](#how-it-works)\n- [Commands](#commands)\n- [Demo](#demo)\n\n## TLDR\n\n1. **Start the cluster**\n   - `make up`\n   - Wait about a minute for everything to boot\n2. **Deploy the app**\n   - `make deploy`\n   - Deploys nginx, only accessible through VPN\n3. **Connect your device**\n   - `make connect`\n   - Run the printed `tailscale up` command on your machine\n4. **Access the app**\n   - `curl http://100.64.0.1:8443`\n   - Or open it in your browser\n5. **Done?**\n   - `make disconnect` to leave the VPN\n   - `make down` to tear everything down\n\n## Requirements\n\n- Docker + Docker Compose\n- [Tailscale client](https://tailscale.com/download) (for connecting your device)\n\n## Versions\n\n| Tool       | Version |\n|------------|---------|\n| Headscale  | 0.28.0  |\n| k0s        | 1.35.1  |\n| Tailscale  | 1.94.2  |\n\n## What's inside\n\n- **Headscale** -self-hosted Tailscale control server (your own VPN)\n- **k0s** -lightweight Kubernetes cluster (controller + worker)\n- **Tailscale** -each node joins the VPN mesh automatically\n- **nginx** -demo app that's only reachable through the VPN\n\n## Architecture\n\n```mermaid\ngraph TD\n    USER_OUT(Outside World) -. blocked .-\u003e SOCAT\n\n    subgraph VPN [Headscale VPN Mesh]\n        HS(Headscale Control Server)\n        LAPTOP(Your Device -100.64.0.x)\n\n        subgraph K8S [k0s Cluster]\n            CTRL(k0s-controller -100.64.0.1)\n            WORKER(k0s-worker -100.64.0.2)\n            SOCAT(socat -100.64.0.1:8443)\n            NGINX(nginx -ClusterIP:80)\n        end\n    end\n\n    HS --- LAPTOP\n    HS --- CTRL\n    HS --- WORKER\n    CTRL -- WireGuard --- WORKER\n    LAPTOP -- curl :8443 --\u003e SOCAT --\u003e NGINX\n```\n\n## How it works\n\n1. Headscale starts first and acts as the VPN control plane\n2. k0s nodes boot up, join the VPN, and get Tailscale IPs (100.64.x.x)\n3. The k0s cluster runs on the Docker network, but the app is exposed only on the Tailscale IP\n4. `socat` binds to the Tailscale IP and forwards to the Kubernetes service\n5. No Tailscale IP = no access\n\n## Commands\n\n| Command           | What it does                        |\n|-------------------|-------------------------------------|\n| `make up`         | Build and start the whole cluster   |\n| `make deploy`     | Deploy nginx behind the VPN         |\n| `make status`     | Show containers, nodes, and pods    |\n| `make users`      | List all users and nodes on the VPN |\n| `make connect`    | Get the command to join the VPN     |\n| `make disconnect` | Disconnect your device from the VPN |\n| `make logs`       | Tail all container logs             |\n| `make down`       | Stop and remove everything          |\n| `make clean`      | Full cleanup including images       |\n\n## Demo\n\n![make up](assets/demo-up.png)\n*`make up` — Building and starting headscale, k0s controller, and worker*\n\n![make status](assets/demo-status.png)\n*`make status` — Containers, headscale nodes, tailscale peers, k8s nodes, and pods*\n\n![make deploy](assets/demo-deploy.png)\n*`make deploy` — Deploying nginx behind the VPN with socat forwarding*\n\n![curl via VPN](assets/demo-curl.png)\n*Accessing the app via `curl` from inside the VPN mesh*\n\n![make connect](assets/demo-connect.png)\n*`make connect` — Getting the tailscale command to join the VPN from your device*\n\n![browser access](assets/demo-browser.png)\n*Browsing the app at `100.64.0.1:8443` — only reachable through the VPN*\n\n![make users](assets/demo-users.png)\n*`make users` — All VPN users and connected nodes including the local device*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanqorithm%2Fheadscale-k0s-vpn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanqorithm%2Fheadscale-k0s-vpn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanqorithm%2Fheadscale-k0s-vpn/lists"}