<!doctype html><htmllang=enclass=no-js><head><metacharset=utf-8><metaname=viewportcontent="width=device-width,initial-scale=1"><metaname=descriptioncontent="An open source, self-hosted implementation of the Tailscale control server."><metaname=authorcontent="Headscale authors"><linkhref=https://juanfont.github.io/headscale/development/setup/install/container/rel=canonical><linkhref=../community/rel=prev><linkhref=../cloud/rel=next><linkrel=iconhref=../../../assets/favicon.png><metaname=generatorcontent="mkdocs-1.6.1, mkdocs-material-9.5.48"><title>Container - Headscale</title><linkrel=stylesheethref=../../../assets/stylesheets/main.6f8fc17f.min.css><linkrel=stylesheethref=../../../assets/stylesheets/palette.06af60db.min.css><linkrel=preconnecthref=https://fonts.gstatic.comcrossorigin><linkrel=stylesheethref="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><script>__md_scope=newURL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script><metaproperty=og:typecontent=website><metaproperty=og:titlecontent="Container - Headscale"><metaproperty=og:descriptioncontent="An open source, self-hosted implementation of the Tailscale control server."><metaproperty=og:imagecontent=https://juanfont.github.io/headscale/development/assets/images/social/setup/install/container.png><metaproperty=og:image:typecontent=image/png><metaproperty=og:image:widthcontent=1200><metaproperty=og:image:heightcontent=630><metacontent=https://juanfont.github.io/headscale/development/setup/install/container/property=og:url><metaname=twitter:cardcontent=summary_large_image><metaname=twitter:titlecontent="Container - Headscale"><metaname=twitter:descriptioncontent="An open source, self-hosted implementation of the Tailscale control server."><metaname=twitter:imagecontent=https://juanfont.github.io/headscale/development/assets/images/social/setup/install/container.png></head><bodydir=ltrdata-md-color-scheme=defaultdata-md-color-primary=whitedata-md-color-accent=indigo><inputclass=md-toggledata-md-toggle=drawertype=checkboxid=__drawerautocomplete=off><inputclass=md-toggledata-md-toggle=searchtype=checkboxid=__searchautocomplete=off><labelclass=md-overlayfor=__drawer></label><divdata-md-component=skip><ahref=#running-headscale-in-a-containerclass=md-skip> Skip to content </a></div><divdata-md-component=announce></div><divdata-md-color-scheme=defaultdata-md-component=outdatedhidden></div><headerclass=md-headerdata-md-component=header><navclass="md-header__inner md-grid"aria-label=Header><ahref=../../..title=Headscaleclass="md-header__button md-logo"aria-label=Headscaledata-md-component=logo><imgsrc=../../../logo/headscale3-dots.svgalt=logo></a><labelclass="md-header__button md-icon"for=__drawer><svgxmlns=http://www.w3.org/2000/svgviewbox="0 0 24 24"><pathd="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg></label><divclass=md-header__titledata-md-component=header-title><divclass=md-header__ellipsis><divclass=md-header__topic><spanclass=md-ellipsis> Headscale </span></div><divclass=md-header__topicdata-md-component=header-topic><spanclass=md-ellipsis> Container </span></div></div></div><formclass=md-header__optiondata-md-component=palette><inputclass=md-optiondata-md-color-mediadata-md-color-scheme=defaultdata-md-color-primary=whitedata-md-color-accent=indigoaria-label="Switch to dark mode"type=radioname=__paletteid=__palette_0><labelclass="md-header__button md-icon"title="Switch to dark mode"for=__palette_1hidden><svgxmlns=http://www.w3.org/2000/svgviewbox="0 0 24 24"><pathd="M128a44000-444400044440004-444000-4-4m010a660
</span></code></pre></div></li><li><p>Download the example configuration for your chosen version and save it as: <code>/etc/headscale/config.yaml</code>. Adjust the configuration to suit your local environment. See <ahref=../../../ref/configuration/>Configuration</a> for details.</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-1-1><aid=__codelineno-1-1name=__codelineno-1-1href=#__codelineno-1-1></a>sudo<spanclass=w></span>mkdir<spanclass=w></span>-p<spanclass=w></span>/etc/headscale
</span></code></pre></div><p>Alternatively, you can mount <code>/var/lib</code> and <code>/var/run</code> from your host system by adding <code>--volume $(pwd)/lib:/var/lib/headscale</code> and <code>--volume $(pwd)/run:/var/run/headscale</code> in the next step.</p></li><li><p>Start the headscale server while working in the host headscale directory:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-2-1><aid=__codelineno-2-1name=__codelineno-2-1href=#__codelineno-2-1></a>docker<spanclass=w></span>run<spanclass=w></span><spanclass=se>\</span>
</span></code></pre></div><p>Note: use <code>0.0.0.0:8080:8080</code> instead of <code>127.0.0.1:8080:8080</code> if you want to expose the container externally.</p><p>This command will mount <code>config/</code> under <code>/etc/headscale</code>, forward port 8080 out of the container so the headscale instance becomes available and then detach so headscale runs in the background.</p><p>Example <code>docker-compose.yaml</code></p><divclass="language-yaml highlight"><pre><span></span><code><spanid=__span-3-1><aid=__codelineno-3-1name=__codelineno-3-1href=#__codelineno-3-1></a><spanclass=nt>version</span><spanclass=p>:</span><spanclass=w></span><spanclass=s>"3.7"</span>
</span><spanid=__span-3-12><aid=__codelineno-3-12name=__codelineno-3-12href=#__codelineno-3-12></a><spanclass=w></span><spanclass=c1># Please change <CONFIG_PATH> to the fullpath of the config folder just created</span>
</span></code></pre></div></li><li><p>Verify headscale is running:</p><p>Follow the container logs:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-4-1><aid=__codelineno-4-1name=__codelineno-4-1href=#__codelineno-4-1></a>docker<spanclass=w></span>logs<spanclass=w></span>--follow<spanclass=w></span>headscale
</span></code></pre></div><p>Verify headscale is available:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-6-1><aid=__codelineno-6-1name=__codelineno-6-1href=#__codelineno-6-1></a>curl<spanclass=w></span>http://127.0.0.1:9090/metrics
</span></code></pre></div></li><li><p>Create a user (<ahref=https://tailscale.com/kb/1136/tailnet/>tailnet</a>):</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-7-1><aid=__codelineno-7-1name=__codelineno-7-1href=#__codelineno-7-1></a>docker<spanclass=w></span><spanclass=nb>exec</span><spanclass=w></span>-it<spanclass=w></span>headscale<spanclass=w></span><spanclass=se>\</span>
</span></code></pre></div></li></ol><h3id=register-a-machine-normal-login>Register a machine (normal login)<aclass=headerlinkhref=#register-a-machine-normal-logintitle="Permanent link">¶</a></h3><p>On a client machine, execute the <code>tailscale</code> login command:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-8-1><aid=__codelineno-8-1name=__codelineno-8-1href=#__codelineno-8-1></a>tailscale<spanclass=w></span>up<spanclass=w></span>--login-server<spanclass=w></span>YOUR_HEADSCALE_URL
</span></code></pre></div><p>To register a machine when running headscale in a container, take the headscale command and pass it to the container:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-9-1><aid=__codelineno-9-1name=__codelineno-9-1href=#__codelineno-9-1></a>docker<spanclass=w></span><spanclass=nb>exec</span><spanclass=w></span>-it<spanclass=w></span>headscale<spanclass=w></span><spanclass=se>\</span>
</span></code></pre></div><h3id=register-machine-using-a-pre-authenticated-key>Register machine using a pre authenticated key<aclass=headerlinkhref=#register-machine-using-a-pre-authenticated-keytitle="Permanent link">¶</a></h3><p>Generate a key using the command line:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-10-1><aid=__codelineno-10-1name=__codelineno-10-1href=#__codelineno-10-1></a>docker<spanclass=w></span><spanclass=nb>exec</span><spanclass=w></span>-it<spanclass=w></span>headscale<spanclass=w></span><spanclass=se>\</span>
</span></code></pre></div><p>This will return a pre-authenticated key that can be used to connect a node to headscale during the <code>tailscale</code> command:</p><divclass="language-shell highlight"><pre><span></span><code><spanid=__span-11-1><aid=__codelineno-11-1name=__codelineno-11-1href=#__codelineno-11-1></a>tailscale<spanclass=w></span>up<spanclass=w></span>--login-server<spanclass=w></span><YOUR_HEADSCALE_URL><spanclass=w></span>--authkey<spanclass=w></span><YOUR_AUTH_KEY>
</span></code></pre></div><h2id=debugging-headscale-running-in-docker>Debugging headscale running in Docker<aclass=headerlinkhref=#debugging-headscale-running-in-dockertitle="Permanent link">¶</a></h2><p>The <code>headscale/headscale</code> Docker container is based on a "distroless" image that does not contain a shell or any other debug tools. If you need to debug your application running in the Docker container, you can use the <code>-debug</code> variant, for example <code>headscale/headscale:x.x.x-debug</code>.</p><h3id=running-the-debug-docker-container>Running the debug Docker container<aclass=headerlinkhref=#running-the-debug-docker-containertitle="Permanent link">¶</a></h3><p>To run the debug Docker container, use the exact same commands as above, but replace <code>headscale/headscale:x.x.x</code> with <code>headscale/headscale:x.x.x-debug</code> (<code>x.x.x</code> is the version of headscale). The two containers are compatible with each other, so you can alternate between them.</p><h3id=executing-commands-in-the-debug-container>Executing commands in the debug container<aclass=headerlinkhref=#executing-commands-in-the-debug-containertitle="Permanent link">¶</a></h3><p>The default command in the debug container is to run <code>headscale</code>, which is located at <code>/ko-app/headscale</code> inside the container.</p><p>Additionally, the debug container includes a minimalist Busybox shell.</p><p>To launch a shell in the container, use:</p><divclass="language-text highlight"><pre><span></span><code><spanid=__span-12-1><aid=__codelineno-12-1name=__codelineno-12-1href=#__codelineno-12-1></a>docker run -it headscale/headscale:x.x.x-debug sh
</span></code></pre></div><p>You can also execute commands directly, such as <code>ls /ko-app</code> in this example:</p><divclass="language-text highlight"><pre><span></span><code><spanid=__span-13-1><aid=__codelineno-13-1name=__codelineno-13-1href=#__codelineno-13-1></a>docker run headscale/headscale:x.x.x-debug ls /ko-app