Tailscale+Headscale+DERP部署实践——基于Docker/1panel
本文最后更新于9 天前,如有错误请在评论区中留言。

headscale_tailscale_corrected

日志

2025-8-11
记录tailscale组网中设备报错无法连接relay server的解决思路,以及tailscale组网中设备连接失败的原因分析
2025-8-7
记录Tailscale部署过程,包含自建headscale,derp服务器,headscle-ui

前言

之前部署过zerotier组网,尝试将ios端加入自建组网,最后失败了。ios端接入特别麻烦,主要有两种方式,具体如下:

方案 描述 具体方法
ios越狱方案 越狱后安装 ZeroTier,然后替换 planet 文件 自行了解
WireGuard方案 使用 WireGuard 接入到 ZeroTier 网络 参考:Issue #192

使用tailscale组网,tailscale客户端支持自定义headscale服务,ios端也可以连接,符合需求。

准备

  • 一台有公网ip的服务器(我这里使用1panel面板)

  • 一个域名(国内服务器需要备案),准备两个二级域名,一个用于headscale和webui,一个用于derp服务

  • 域名证书(基于1panel获取SSL证书的方式,之前文章中有写,点击传送,文章页面Ctrl+F搜索SSL

  • 开启对应端口防火墙,本次部署端口如下(可自行在配置文件中修改)

    协议 端口 作用
    TCP 58080 headscale服务器
    TCP 57070 headscale-admin(webui)
    TCP 56060 derp中继服器
    UDP 3478 derp STUN(发现公网映射、判断 NAT 类型)

部署

首先在服务器上安装dockerdocker-compose

然后,在服务器上创建一个目录,用于存放相关配置文件,结构树如下

headscale
├── docker-compose.yaml
├── dockerfiles
│   └── derp.Dockerfile
└── headscale
    └── config
        ├── config.yaml
        └── derp.yaml

配置config.yaml

注意
由于配置文件内容过多,这里只展示config.yaml文件中需要修改的内容

复制配置文件模版:config-example.yaml,将其粘贴至config.yaml文件中并修改其中部分内容,如下:

...

server_url: https://headscale.example.com  #这里修改为你自己的headscale server域名,注意必须带https://,否则容器会一直重启
listen_addr: 0.0.0.0:8080                  #0.0.0.0才能监听到,可以用公网ip

...

prefixes:
  v4: 100.64.0.0/10          #ip段范围,可以自定义
  #v6: fd7a:115c:a1e0::/48   #我用不到v6 所以注释

...

derp:
  server:
    enabled: false           #不启用官网自带的derp
  urls:                      #这里留空,下面的paths路径会配derp server的地址
  paths:
    - /etc/headscale/derp.yaml

...

dns:
  magic_dns: false           #按需使用,我这里暂时关闭,如配合rustdesk使用,需要配置
  override_local_dns: false  #改为false不覆盖本地DNS,确保内部域名生效

...

randomize_client_port: true  #客户端将不再固定使用 UDP/41641 作为 WireGuard 流量的源端口,而是随机挑选高位端口,以绕过仅拦截 41641 的防火墙

配置derp.yaml

这里有只有hostname需要修改:

regions:
  999:
    regionid: 999
    regioncode: cnbj
    regionname: Aliyun Beijing
    nodes:
      - name: derp
        regionid: 999
        hostname: derp.example.com  #这里修改为你自己的derp server域名
        ipv4: 123.123.123.123       #这里填自己服务器的ip,确保连接
        stunport: 3478
        port: 443                   #注意这里必须是443,组网设备访问derp服务器,需要走443,如果你改为56060端口,存在特定网络环境无法访问到derp服务器的情况
        stunonly: false

构建derp镜像

我这里手动构建最新的derp镜像,当然也有现成的derp镜像使用,例如:ghcr.io/yangchuansheng/derper:latest

在derp.Dockerfile文件中编辑如下:

FROM golang:alpine AS builder

WORKDIR /app

#https://tailscale.com/kb/1118/custom-derp-servers/
RUN go env -w GOPROXY=https://goproxy.io,direct
RUN go install tailscale.com/cmd/derper@latest

FROM alpine:3.21

WORKDIR /app

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
RUN apk add --no-cache ca-certificates iptables iproute2 ip6tables curl && \
    mkdir /app/certs

RUN apk add gcompat

ENV DERP_DOMAIN=your-hostname.com
ENV DERP_CERT_MODE=letsencrypt
ENV DERP_CERT_DIR=/app/certs
ENV DERP_ADDR=:443
ENV DERP_STUN=true
ENV DERP_STUN_PORT=3478
ENV DERP_HTTP_PORT=80
ENV DERP_VERIFY_CLIENTS=false
ENV DERP_VERIFY_CLIENT_URL=""
COPY --from=builder /go/bin/* /usr/local/bin/

CMD /usr/local/bin/derper --hostname=$DERP_DOMAIN \
    --certmode=$DERP_CERT_MODE \
    --certdir=$DERP_CERT_DIR \
    --a=$DERP_ADDR \
    --stun=$DERP_STUN \
    --stun-port=$DERP_STUN_PORT \
    --http-port=$DERP_HTTP_PORT \
    --verify-clients=$DERP_VERIFY_CLIENTS \
    --verify-client-url=$DERP_VERIFY_CLIENT_URL

使用docker-compose部署

编辑docker-compose.yaml文件,文件包含了 headscale、headscale-admin、derp、client,请按需进行修改。

networks:
  private:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.200.0/24 #不能与Docker网络的地址空间发生重叠,例如docker已有172.20.0.0,就会与这里subnet重复

services:
  server:
    image: headscale/headscale:latest-debug
    container_name: headscale-server
    networks:
      - private
    volumes:
      - ./headscale/config:/etc/headscale
      - ./headscale/data:/var/lib/headscale
      - ./headscale/run:/var/run/headscale
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
    ports:
      - 58080:8080  #自行修改端口
    command: serve
    restart: unless-stopped
    depends_on:
      - derp

  webui:
    image: goodieshq/headscale-admin:latest
    container_name: headscale-ui
    networks:
      - private
    environment:
      PORT: 7070   #自行修改端口
    ports:
      - 57070:7070 #自行修改端口
    volumes:
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
    restart: unless-stopped

  derp:
    build:
      context: ./dockerfiles
      dockerfile: derp.Dockerfile
    container_name: headscale-derp
    networks:
      - private
    environment:
      DERP_DOMAIN: derp.example.com    #修改为你自己的derp server的域名
      DERP_ADDR: :6060                 #自行修改端口
      DERP_CERT_MODE: letsencrypt
      #DERP_CERT_MODE: manual          #如果需要手动,需要配置证书
      DERP_VERIFY_CLIENTS: 'true'
    ports:
      - 56060:6060                     #derp port, TCP,自行修改端口
      - 3478:3478/udp                  #STUN port, UDP,自行修改端口
    volumes:
      #- 证书路径:/app/certs/           #注意,这个是手动模式使用
      - ./tailscale:/var/run/tailscale
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
    restart: unless-stopped

  client:
    image: tailscale/tailscale:stable
    container_name: tailscale-client
    network_mode: host
    privileged: 'true'
    environment:
      TS_EXTRA_ARGS: --netfilter-mode = off
    volumes:
      - ./tailscale:/var/run/tailscale
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
      - /var/lib:/var/lib
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin
      - sys_module
    command: tailscaled
    restart: unless-stopped

完成上述所有步骤后,在headscale路径下,使用下面的命令一键部署 headscale、derp 和 webui。关于docker-compose的使用可以参考:Docker Compose的使用 – Clif's Blog

sudo docker-compose up -d

配置反向代理

注意

关于tailscale组网中设备报错无法连接relay server的解决思路,以及tailscale组网中设备连接失败的原因分析:

先说结论:我不小心使用了56060作为derp的外部端口,被网络环境墙掉了,导致无法ping通,应当使用443端口。更精确地说:对客户端暴露的端口最好是 443(写进 derp.yamlport 也最好是 443),下面是分析:

这个报错大概率是因为:对Tailscale客户端来说,连DERP一定是走TLS的,绝不能让客户端直接碰到明文端口,否则就会出现:relay server 无法连接组网设备ping不通等报错。这里有两种情况:

  1. TLS在Nginx 结束(HTTP 反代)
  2. TLS在derper 结束(stream 透传)

方案1:使用 Nginx 的 location / … proxy_pass 做http反代,直接将443反代给端口56060,这里可以不用让derp走TLS,虽然后端走的是明文http,但是不影响使用。

方案2:如果你非要让derp走TLS,需要手动配置证书(自动的我没有跑通),并且让derp服务独占443端口,这样TLS握手原样到达 derper。但是1panel面板中的openresty已经占用了443端口,怎么办?这个时候需要使用nginx stream(L4 透传),将derp.example.com:443透到本机56060端口来,同时,服务器原本的网站服务透到4443端口或其他端口。(再吐槽一下,这里千万不要让客户端直接连56060或其他,derp.yamlport 必须是443,博主脑子抽了,定位错误花了好些时间。因为56060这类似的端口容易被网络环境墙掉,不能稳定连接),附上nginx stream的配置:

stream {
  #1) 按 SNI 选择后端(把 derp 的域名改成你的,比如 derp.example.com)
  map $ssl_preread_server_name $backend {
    derp.example.com   derp_backend;   #访问 derp.example.com → 走 derper
    default            web_backend;    #其它域名 → 走网站(4443)
  }

  #2) derper 后端:转到宿主 56060(Docker 再转容器 6060)
  upstream derp_backend {
    server 127.0.0.1:56060;
  }

  #3) 网站后端:转到 4443(上一步你已把站点改去监听 4443)
  upstream web_backend {
    server 127.0.0.1:4443;
  }

  #4) 统一在 443 上接入(TCP 层,纯透传;不要写 ssl)
  server {
    listen 0.0.0.0:443 reuseport;
    proxy_pass $backend;
    ssl_preread on;
    proxy_timeout 3600s;
    proxy_connect_timeout 10s;
  }
}

在1panel中配置反向代理headscale.example.comderp.example.com(之前文章中有写,点击传送,文章页面Ctrl+F搜索反向代理

在1panel的headscale.example.com网站中,继续配置webui的反向代理:

image-20250808222511285

测试

完成了上述所有步骤后,在浏览器中访问headscale.example.comderp.example.com,如果看到下面的页面,说明部署成功了。

headscale.example.com页面:

image-20250808191903759

derp.example.com页面:

image-20250808192206887

使用

Web-UI使用

这里只介绍基础功能的使用,其他功能自行探索

进入headscale-server容器中,获取API key:

docker exec -it headscale-server headscale apikeys create

打开headscale.example.com,填入apikey,点击保存设置,出现左侧菜单栏就算成功了:

image-20250808193313214

创建用户:

image-20250808194857640

点击Creat创建preAuth Keys,这个key有时效,会自动过期:

image-20250808195006082

进入Deploy栏,选择刚刚的preAuth Keys,点击上方可以复制代码使用。

image-20250808195339108

客户端加入

以Linux为例,进入tailscale客户端容器:

sudo docker exec -it tailscale-client /bin/sh

在容器内使用刚刚在Delpoy栏复制的命令即可,这里列出一些常用的命令,其他指令可以参考官方手册或问AI:

#加入headscale服务器
tailscale up --login-server <server_url> --hostname <设备名自取 需要英文> --accept-routes=true --accept-dns=false --authkey <刚刚生成的 authkey>

#查看derp服务
tailscale netcheck

#查看节点状态(是否打洞成功P2P还是走的derp服务等)
tailscale status

#ping测试
tailscale ping <headscale 分配的ip>

至于其他平台的客户端基本都有界面操作,基本没难度,自行探索一下就行。

参考

使用docker-compose一键部署headscale、derper和webui | 小桶

TailScale开源组网(服务端+headscale-admin管理界面) 全平台实现_网络存储

Tailscale+Headscale+自建Derp踩坑记录 - yyhhyy's blog

Tailscale DERP Docker版教程(国内 无需域名 IP+Port) - 开发调优 - LINUX DO

Tailscale 基础教程:Headscale 的部署方法和使用教程 - 米开朗基杨 - 博客园

 

如果您觉得本博客内容有所帮助,可以打赏一下吗~,此打赏将会用于博客维护运营,十分感谢!
作者:Clif
版权声明: 本博客所有文章除未特别声明外,均采用CC BY-NC-SA 4.0协议。转载请注明文章地址及作者!
暂无评论

发送评论(请正确填写邮箱地址,否则将会当成垃圾评论处理) 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇