1. 先搞清楚 go2rtc 到底能做什么,再决定要不要部署

1.1 摄像头监控的“协议困局”,比你想象的还烦

玩摄像头的人应该都有这种体验:家里装了七八个不同牌子的 IPC,每个都有自己的 App,海康的用海康的客户端,TP-LINK 的用 TP-LINK 的 App,大华的又得装另一个。想统一看?不好意思,各家的协议和端口互相不认。就算你技术底子好,知道摄像头背后跑的是 RTSP 标准协议,真去接的时候也会被坑得体无完肤:有的摄像头 RTSP 路径是 /Streaming/Channels/101 ,有的是 /live/ch0 ,有的是 /h264/ch1/main/av_stream ,网上搜到的教程根本对不上。

好不容易用 VLC 能打开 RTSP 流了,你又发现新问题: RTSP 协议在浏览器里几乎无法直接播放 。Chrome、Firefox、Edge 从一开始就不支持 RTSP,手机上更不用想。你要是想做个网页端监控大屏,要么自己写个播放器搞 WebSocket 转换,要么转成 HLS 走 HTTP-FLV,要么用 WebRTC 低延迟方案——但这几个方案没有一个是开箱即用的,全都要自己搭服务、写代码、处理音视频转码。到这个阶段,你已经不是在装监控了,你是在写一套流媒体系统。

go2rtc 这个名字里的 2rtc 不是乱起的,它最核心的能力就是“转到 WebRTC”,也就是把各种协议的视频流统一“翻译”成浏览器能原生播放的 WebRTC 流。你不再需要安装任何插件,打开网页就能看,延迟还能控制在 300 到 500 毫秒以内。但它能干的不只是 WebRTC,RTSP、RTMP、HLS、MJPEG、MP4、ONVIF 这些主流协议它全都吃,输入输出都能转,等于在摄像头和你之间架了一座协议翻译桥。

1.2 go2rtc 的能力边界到底在哪里

我先给一个整体认知,方便你判断它适不适合你的场景:

能力 go2rtc 支持的协议或功能 典型用途
拉流接入 RTSP、RTMP、HTTP-FLV、HLS、MJPEG、ONVIF、本地文件、USB 摄像头 接各种品牌 IPC、NVR、DJI 无人机、树莓派摄像头
推流输出 RTSP、RTMP、HLS、LL-HLS、MJPEG、WebRTC、MP4 网页直播、低延迟预览、录像归档
转码能力 内置 ffmpeg 调用、硬件加速(VAAPI、NVENC、QSV) 摄像头不支持 H.264 时转码、多路并发降码率
动检集成 输出 WebRTC 流给 Frigate NVR 做 AI 检测 Home Assistant 智能家居监控方案
API 能力 完整的 HTTP REST API、流列表管理、Web UI 二次开发、自动切换画面、状态监控

我自己的感受是,go2rtc 最舒服的地方不是某个单一协议有多强,而是“把一摊子杂事合成一个入口”。原来的方案是你得部署三个服务:一个负责拉 RTSP 转发,一个负责转 HLS,一个负责 WebRTC 网关,互相之间还要配置对接。go2rtc 是单个二进制文件,跑起来就全干了,配置也好写,就是一个 YAML。

适合谁来用? 你如果是折腾智能家居、准备搞 Frigate 做人体识别、或者想在公司/学校搭一个多摄像头统一监控页面的开发者,这套方案几乎是不二之选。你如果是完全不懂技术的用户,想给家里装监控,那用它可能稍微有点门槛,但你只要照着下面的配置抄,20 分钟内也能跑起来。

1.3 为什么选择 Docker 而不是裸机安装

go2rtc 官方也提供单文件可执行程序,直接下载就能跑,为什么我还要推荐 Docker 部署?原因有三条:

第一, 隔离和清理 。go2rtc 的依赖虽然不多,但它在运行过程中会拉 ffmpeg、可能会跑转码进程、还会创建配置和录像文件。放在 Docker 容器里,所有文件和进程都圈在容器内部,不污染宿主机。哪天不用了, docker rm -f go2rtc 一条命令连文件残渣都清干净了。

第二, 升级和回滚容易 。go2rtc 迭代很快,作者经常加新协议、修 WebRTC 的 BUG。裸机部署时升级就是覆盖二进制,虽然也不难,但你要自己关注 release 版本;Docker 部署只要把镜像 tag 一换, docker compose up -d 就完成了升级,想回滚就再换回旧 tag,一分钟内搞定。对于跑在生产环境里的监控系统来说,这个“可回滚”极其重要。

第三, 跨平台一致性 。你可以在 Windows 上开发测试、在 Linux 服务器上正式跑、在 NAS 上再放一份,只要都是 Docker 环境,行为完全一致。裸机部署的话,Windows 上还有各种路径和防火墙差异,调试成本高不少。

当然了,Docker 部署也有自己的坑——最常见的就是 Windows 下 Docker Desktop 本身起不来、国内拉镜像超时、容器网络模式选错导致 WebRTC 打洞失败。这一篇我会手把手把这些坑全踩一遍再填上,你照着走基本不会再兜圈子。


2. Docker 部署 go2rtc 实操:从环境准备到真正跑起来

2.1 开始前检查:虚拟化、Docker 版本、网络环境

先说环境检查。这一步很多人会跳过,但 80% 的安装报错都出在环境没准备好 。

如果你用的是 Windows,最常见的报错就是:

Docker Desktop failed to start because virtualisation support was not detected.

这个报错的本质是 Docker Desktop 依赖虚拟化技术来跑 Linux 容器,但你的机器可能没开启虚拟化,或者开启了但没被系统识别。检查顺序如下:

  1. BIOS 里开启 CPU 虚拟化 。重启机器进 BIOS(一般是开机按 Del 或 F2),找到 Intel Virtualization Technology(Intel VT-x)或 AMD SVM,确认是 Enabled。这一步必须做,软件层面怎么配置都绕不过硬件开关。
  2. Windows 功能里启用相应的虚拟化平台 。控制面板 → 程序和功能 → 启用或关闭 Windows 功能,勾选 Hyper-V 和 适用于 Linux 的 Windows 子系统 。如果你用的是 Windows 10/11 家庭版,里面没有 Hyper-V 选项,那就需要在 PowerShell(管理员)里执行命令安装 WSL2:
wsl --install

这个命令会同时启用 WSL2 和虚拟机平台,装完重启电脑。新版 Docker Desktop 是跑在 WSL2 后端的,所以这一步搞定后,上面那个 virtualisation support 的报错基本就消失了。

  1. 下载并安装 Docker Desktop 。从 Docker 官网下载稳定版,安装时保持默认设置即可。装完以后 Docker Desktop 会提示你先退出再重启,照做就行。
  2. 验证 Docker 是否可用 。打开 PowerShell 或 CMD,运行:
docker version

如果能看到 Client 和 Server 两段版本信息,说明 Docker 的守护进程已经正常跑起来了。这里要特别说明: Client 有信息不代表 Server 正常 ,很多人第一次运行只看到 Client 段,然后下面报 failed to connect to the docker api,这就是典型的前后端没连上。

docker 镜像源的问题也要提一下。国内直接拉 go2rtc 的官方镜像大概率超时,所以先配置 Daemon 镜像加速。打开 Docker Desktop 设置 → Docker Engine,在 JSON 配置里加一段:

{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com"
  ]
}

保存之后 Docker Desktop 会自动重启 daemon。注意这个配置只影响从 Docker Hub 拉镜像的速度,不影响你运行任意镜像本身,所以放心配。

2.2 镜像选型:ghcr.io 还是 Docker Hub

go2rtc 的镜像有两个来源,官方推荐的是 GitHub Container Registry(ghcr.io),Docker Hub 上也有一份,但名字和渠道容易搞混。很多人第一次搜 go2rtc,直接 docker pull go2rtc/go2rtc ,结果拉到一个个人维护的镜像,行为跟官方不太一样,排查起来很痛苦。

正确的镜像地址有两个(任选其一):

  • ghcr.io/alexxit/go2rtc:latest
  • docker.io/alexxit/go2rtc:latest

我这边的实际体验是:如果你的服务器在境外的云服务商,直接拉 ghcr.io 的镜像非常快;如果机器在国内,Docker Hub 路径配合上面的国内镜像加速反而更稳。所以我的建议是: Docker Hub 优先,失败再切 ghcr 。拉镜像的命令:

docker pull alexxit/go2rtc:latest

拉完可以用 docker images 确认镜像已经存在。这里有个小技巧:在拉镜像之前先 docker search go2rtc 看一眼,如果你看到的镜像名有一堆下划线数字后缀或者明显是个人账号,就别用了,绕过它。

2.3 最小化部署:一条 docker run 命令直接跑起来

我不想一开始就扔出一大段 docker-compose 配置把你砸晕,先来一条最简命令,把 go2rtc 跑起来看看是什么样子。打开终端执行:

docker run -d \
  --name go2rtc \
  --restart unless-stopped \
  -p 1984:1984 \
  -p 8554:8554 \
  -p 8555:8555/udp \
  -v /path/to/go2rtc/data:/config \
  -e TZ=Asia/Shanghai \
  alexxit/go2rtc:latest

如果你对 Docker 还不太熟,我给你逐个参数解释一下:

  • -d :后台运行容器,终端不阻塞。
  • --name go2rtc :给容器起名字,后面查看日志、进入容器都用这个名字。
  • --restart unless-stopped :容器异常退出或宿主机重启后,Docker 会自动拉起容器。监控服务最怕断电后没人去启动,这个参数必须加。
  • -p :端口映射。宿主机 1984 端口对应容器 1984(Web 管理界面),8554 对应 RTSP 输出端口,8555 UDP 对应 WebRTC 的媒体传输端口。注意 8555 必须用 /udp 标记,WebRTC 走的是 UDP,漏掉这个玩不了低延迟播放。
  • -v /path/to/go2rtc/data:/config :把容器里的配置文件目录挂载到宿主机,容器升级后配置不丢。 /path/to/go2rtc/data 要换成你自己的路径,Windows 下可以是 D:/docker/go2rtc 这种写法。
  • -e TZ=Asia/Shanghai :设置时区,防止日志时间和录像文件名差 8 个小时。

跑完以后打开浏览器访问 http://localhost:1984 ,你会看到一个简单的 Web UI。第一次启动时它也允许直接通过界面添加摄像头,非常友好。不过在实际使用中,我更推荐你直接用配置文件来定义,因为界面虽然方便,但配置多了以后全靠手点很烦,而且不可回溯。

2.4 进阶部署:docker-compose 完整配置示例

当你决定正式在生产环境部署 go2rtc,或者要同时管理多个容器(比如 go2rtc + Frigate + Home Assistant),就别再用 docker run 了,上 docker-compose。它会帮你把配置写进文件、版本化管理,以后迁移机器只需要把目录带走。

下面是我验证过的 compose 文件,你可以直接复制:

version: "3.8"

services:
  go2rtc:
    image: alexxit/go2rtc:latest
    container_name: go2rtc
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./go2rtc/config:/config
    environment:
      - TZ=Asia/Shanghai

等等,我用了 network_mode: host 而不是之前的 ports 映射。这是一个重要的取舍点,我专门展开讲一下。

host 网络模式 的意思是容器直接使用宿主机的网络栈,不经过 Docker 的虚拟网桥。这对 go2rtc 有两个好处:

第一,WebRTC 需要在局域网内做候选地址协商。默认桥接模式下,容器内部看到的 IP 是 172.x 之类,摄像头和浏览器如果直接访问这个内网地址,根本连不上。用 host 模式后容器直接用宿主机的局域网 IP,协商和连通都顺理成章。

第二,go2rtc 打洞穿透时需要知道自己的公网 IP,桥接模式下 Docker 每次重建容器都可能换 IP,非常影响后续访问。

代价是什么?host 模式下你没法像桥接模式那样在同一台机器上跑多个 go2rtc 各占不同端口,也没法依赖 Docker 的防火墙隔离。 如果你只是在家里用、同一台机器不会跑多个 go2rtc,那 host 模式是更优解;如果你是在云服务器上跟别的东西共享一台机,建议回到桥接模式配合显式端口映射 。

如果你用的是桥接模式,完整示例是这样的:

version: "3.8"

services:
  go2rtc:
    image: alexxit/go2rtc:latest
    container_name: go2rtc
    restart: unless-stopped
    ports:
      - "1984:1984"
      - "8554:8554"
      - "8555:8555/udp"
    volumes:
      - ./go2rtc/config:/config
    environment:
      - TZ=Asia/Shanghai

在 compose 文件所在目录下执行:

docker compose up -d

然后用 docker compose logs -f go2rtc 实时看日志。你会看到 go2rtc 打印出监听地址和启动信息。如果是下面这种输出,说明服务已经就绪:

INFO: 1984/tcp listening on [::]:1984
INFO: 8554/tcp listening on [::]:8554
INFO: 8555/udp listening on [::]:8555

3. 接入摄像头与多协议输出:把流“翻译”成任何端都能看的格式

3.1 RTSP 接入与参数调优

服务跑起来了,接下来最重要的事:把摄像头接进来。go2rtc 的配置文件默认是 /config/go2rtc.yaml ,你打开后在里面定义一个自定义流名,然后指向摄像头的 RTSP 地址,格式如下:

streams:
  front_door: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101
  backyard: rtsp://admin:password@192.168.1.101:554/live/ch0
  garage: rtsp://admin:password@192.168.1.102:554/h264/ch1/main/av_stream

保存后不要重启容器,用下面的命令重新加载配置:

docker exec go2rtc kill -HUP 1

这里解释一下原理:go2rtc 收到 SIGHUP 信号后会热加载配置,不会中断正在播放的流。这个细节很多人不知道,每次改配置都 docker restart go2rtc ,结果所有正在看的画面全部卡顿几秒。你在生产环境里至少应该先用 SIGHUP,确认新流也能拉起来再决定要不要重启。

改完配置后,打开 Web UI 的 streams 页面,你应该能看到这三个流以及它们的延迟状态和帧率。如果某个流一直显示 404 或者连接失败,大概率是 RTSP 的路径不对或者摄像头限制同时连接数量。这时候可以先用 VLC 验证一下原始地址能不能播放,排除摄像头自身的问题,再来排查 go2rtc。

参数调优 :go2rtc 拉取 RTSP 流默认用的是 UDP 传输,但在 Wi-Fi 或跨网段的场景下 UDP 丢包会导致画面花屏、频繁重连。我实际遇到不止一次,有线连接好好的,换成 Wi-Fi 摄像头就卡成一帧一帧。解决方案是在流地址后面加 #tcp 强制走 TCP 传输:

streams:
  front_door: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101#tcp

或者归一配置强制所有 RTSP 走 TCP:

rtsp:
  transport: tcp

有人会担心 TCP 传输重新组包会不会变大延迟,实际上在局域网内这个延迟差异微乎其微,但稳定性的提升是肉眼可见的。我的习惯是:有线的用默认 UDP,无线的强制 TCP。

3.2 ONVIF 自动发现与免配置接入

如果你家里不止三五个摄像头,而是一二十个,手动一个个写 RTSP 地址会非常痛苦,而且不同品牌路径规则都不一样,写错一个就要排查半天。go2rtc 支持 ONVIF 协议,你可以把 RTSP 地址替换成 ONVIF 的设备地址,格式:

streams:
  cam01: onvif://admin:password@192.168.1.108

配置里这样写后,go2rtc 会主动向该设备的 ONVIF 服务发送探测请求,自动获取摄像头支持的媒体文件、编码格式、RTSP 路径。这对海康、大华、TP-LINK、瑞尼等支持 ONVIF 标准的设备通常有效。

ONVIF 自动接入有一个注意点: 部分厂家的摄像头为了封闭生态,会在 ONVIF 协议里故意不返回正确的 RTSP URL,或者需要额外认证 。遇到这种情况,你就只能老老实实通过录像机的网络设置页面去查流地址了。

我认为 go2rtc 做这类自动发现最有价值的地方还是 Web UI 里的“自动发现”功能——它会扫一遍局域网,把可用的 ONVIF 设备全部列出来,然后提供一键添加。对于第一次部署、又不想慢慢敲配置的人来说,这个入口比 YAML 更直观。

3.3 用 WebRTC 实现低延迟网页播放

go2rtc 最强的杀手锏就是 WebRTC 输出。

用浏览器打开 http://localhost:1984/webrtc.html ,输入你要播放的流名(比如 front_door),点 play,它就能在浏览器里直接播放。这里没有插件、没有额外的播放器,WebRTC 是浏览器原生的能力,视频编码采用 H.264 时浏览器能硬解,CPU 占用极低。

WebRTC 的超低延迟来自它使用 UDP 作为传输层,摒弃了 HLS 的 m3u8 切片机制。多说一点原理:传统的 HLS 把视频切成几秒一个的 ts 文件,通过 HTTP 下载,这个切片-下载-拼接的过程至少得有几秒延迟,慢的时候 10 秒以上。WebRTC 则是一次性建立点对点 UDP 通道,视频帧实时传,不做切片存储,延迟压缩到 300 到 500 毫秒。在监控场景里这个差别很致命,比如看门口有人送快递,你要的是实时看到人走到哪了,而不是看到一个 10 秒前的画面。

如果你要在自己的网页中集成 go2rtc 的播放,官方给的方案就是 WebRTC。大致流程是:

  1. 从 go2rtc 的 API 获取 SDP offer。
  2. 用浏览器原生 RTCPeerConnection 建立连接。
  3. 把远端流绑到 <video> 标签上播放。

官方文档里有一个简化方法:用 go2rtc 内置的 webrtc.html 页面 URL 参数直接指定流名,比如这样访问就是直接播放某个摄像头:

http://localhost:1984/webrtc.html?src=front_door

如果延迟要求没那么高、只是想做一个兼容性极好的网页直播(比如给外部用户看),那也可以直接用 HLS。go2rtc 默认会为每条流自动生成对应的 HLS 地址:

http://localhost:1984/hls/front_door.m3u8

HLS 的优势是所有设备都能播,iOS 的 Safari 原生支持,第三方播放器如 VLC、PotPlayer 都能放,代价是延迟高。所以在同一个平台上,你可以让内网的人走 WebRTC 看实时画面,外网用户走 HLS 看低精度版本,两者并存互不冲突。

3.4 与 Frigate 和 Home Assistant 联动(扩展能力)

如果你的目标不只是“在网页上看监控”,还想要人形检测、车辆识别、通知推送,那 go2rtc 通常会搭配 Frigate NVR 使用。go2rtc 和 Frigate 的联动逻辑是这样的:

  • go2rtc 负责拉取摄像头的 RTSP 流,并把 WebRTC 视频流输出到浏览器。
  • Frigate 通过 go2rtc 的 REST API 订阅同一路流,从中抽帧做 AI 检测。

Frigate 在配置里支持直接声明 go2rtc 的流地址,比如 Frigate 的 config.yml 里这样写:

go2rtc:
  streams:
    front_door: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101#tcp

配合 Home Assistant 的话,Hass 里的 stream 组件可以直接访问 go2rtc 的 HLS 地址,这样智能家居管理界面、手机 App、NVR 检测、浏览器实时预览全部串在一起,一个 go2rtc 不仅仅是个流媒体中转站,而是整个家庭监控系统的中枢。这个闭环搭好之后,你以后加摄像头就是改一行 YAML 的事。


4. 常见问题与排查技巧实录

4.1 Docker Desktop 启动失败:virtualisation support 与 failed to connect 类报错

这两个报错占了我帮助他人排障的六成以上。先说 virtualisation support not detected 。除了前面提到要在 BIOS 开 VT-x/SVM、在 Windows 功能里启用 Hyper-V 和 WSL2 之外,还有一种可能:你已经开了 Hyper-V,但 Docker Desktop 用的 WSL2 内核没更新。执行:

wsl --update

这个命令会把 WSL2 内核更新到最新,很多奇奇怪怪的启动失败都能解决。理论上讲,WSL2 发布这么多年,新的 Windows 版本基本都默认携带并启用了虚拟化平台,所以如果装好后重启一次还不行,多半是 BIOS 没开对,或者机器比较老不支持虚拟化。老电脑的话,硬件层面的限制软件救不回来,只能换新机器或用云服务器。

再说 failed to connect to the docker api at npipe:////./pipe/dockerdesktoplinuxen 。这个报错通常出现在 Windows 下执行 docker ps 时。原因一般是 Docker Desktop 的前端界面没起来,或者 WSL2 内核坏了。按以下顺序排查:

  1. 确认 Docker Desktop 图标出现在系统托盘,且状态是 “Engine running”。
  2. 如果显示 “Docker Engine Stopped”,点一下重启引擎,等一两分钟。
  3. 如果重启不行,在 PowerShell 执行 wsl --shutdown ,把 WSL2 彻底停掉再重新启动 Docker Desktop。
  4. 最后“核弹”方案是退出 Docker Desktop,在设置里点 “Clean / Purge data”,然后重装。这个操作会清掉所有镜像和容器,只适合最后使用。

4.2 镜像拉取超时或过慢

go2rtc 镜像本身才几十 MB,拉取不应该很慢。如果你发现 docker pull alexxit/go2rtc 卡住或报 timeout,多半是因为你机器上直连 Docker Hub 的网络质量差。解决思路很直接:配置 registry-mirrors(前面已经写过),或者干脆用 ghcr.io 源替代。需要提醒的是,很多网上流传的镜像加速地址时效性很差,今天能用明天就失效了。我验证过还能用的加速地址包括 https://docker.1ms.run 、 https://docker.xuanyuan.me 等,但你自己用之前先 docker pull hello-world 试一下,有效再继续。

4.3 RTSP 接入了但画面一直缓冲或黑屏

这类问题在排查上最耗时,但原因通常是下面几个:

症状 主要原因 解决方案
流一直缓冲、无法播放 RTSP 走 UDP 丢包严重 在流地址后加 #tcp 强制 TCP
画面有声音没图像 摄像头输出 H.265/HEVC,但你的播放器/浏览器不支持 在 go2rtc 里配置 ffmpeg 转码为 H.264
能看到第一帧但很快卡死 摄像头限制同时连接数,被其他客户端占用 降低摄像头端最大连接数限制,或给 go2rtc 分配独立账号
在线但延迟越来越高 go2rtc 处理不过来,或源端码率过高 在 go2rtc 中加 ffmpeg 转码降码率

关于 H.265 转码,这是绕不开的一个话题。现在很多新摄像头默认输出 H.265,压缩率高一倍,但 WebRTC 和浏览器对 H.265 的支持并不统一,Chrome 桌面版能解部分硬件的 H.265,Safari 可以,Android 上很多浏览器不行。最省心的做法是在 go2rtc 中配置一个 ffmpeg 转码流,强制转成 H.264。配置示例:

streams:
  front_door:
    - rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101#tcp
    - ffmpeg:front_door#video=copy#video=h264#hardware

我来解释一下这串参数的含义: video=copy 表示尽量直接复制原始视频编码,不重新编码; video=h264 表示输出目标是 H.264; hardware 表示优先尝试使用硬件加速。这个方案是兜底性质的——go2rtc 会优先尝试用直连方式去拉流,如果发现源是 H.265 而播放端不支持,就会自动转码。唯一需要注意的是硬件加速需要容器能访问 GPU 设备,如果是在 Docker Desktop 里跑 Windows 容器,这一步可能受限,那就只能纯 CPU 转码,建议降低同时播放的路数。

4.4 局域网能看、外网/web端不能看(WebRTC 候选地址问题)

这是 go2rtc 从新手到进阶最容易踩的坑。你的 go2rtc 跑在家里的 NAS 上,内网用网页看一切都正常,但你把 http://你的公网IP:1984 暴露出去,打开网页后画面永远起不来。原因是 WebRTC 在建立连接时,会交换“候选地址”(ICE candidates),浏览器拿到的候选地址里只有内网 IP,没有公网 IP,它不知道应该往哪里发数据包。

解决方法是给 go2rtc 指定公网候选地址或让它自动探测。在 go2rtc 配置里加 webrtc 段:

webrtc:
  candidates:
    - 你的公网IP:8555

如果公网 IP 是动态的,可以用域名拼接端口,go2rtc 会自动解析:

webrtc:
  candidates:
    - yourdomain.com:8555

然后记得在路由器/防火墙里放行 UDP 8555 端口,并且把该端口转发到 go2rtc 所在主机。这个端口转发必须同时覆盖 TCP 和 UDP,很多人漏了 UDP 转发,导致 WebRTC 永远握手失败。

但这里我要多说一句安全建议: 不要直接把 go2rtc 的 Web UI 暴露到公网 。默认情况下,go2rtc 没有身份认证,谁能访问你的 Web UI 谁就能查看所有摄像头画面,还能通过 API 查看流配置。如果你确实需要远程观看,正确姿势是配置 go2rtc 内置的登录认证,或者在它前面加一个带身份认证的反向代理(nginx 的 proxy_pass + Basic Auth 比 go2rtc 自带密码更灵活),再或者用 Tailscale 这类组网方案把设备放进虚拟内网。公网裸奔的监控系统等于把大门钥匙挂在门把手上,这种低级错误不该犯。

4.5 容器老挂掉或者重启后配置丢失

监控服务追求的首先是稳定。docker-compose 里 restart: unless-stopped 已经保证了容器出问题后会被自动拉起,但如果你发现容器的配置改完重启后“丢失”了,那多半是挂载卷路径写错了。

先明确挂载规则: -v /宿主机路径:/config 的冒号右边是容器内路径,不能改,是 go2rtc 固定的配置目录;冒号左边是宿主机路径,由你指定。如果你把宿主机路径指向了一个错误目录,容器里写出的配置就会“消失”(实际上写去了别的地方)。排查方法很简单:

docker inspect go2rtc --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{end}}'

这条命令会打印容器实际挂载的宿主机路径和容器内路径,对比一下就知道是不是路径写错了。另外,容器日志也会同步打印配置目录位置,启动时看到类似 config: /config ,如果它显示的是容器内默认目录而不是挂载卷,说明你的 -v 参数没生效。

4.6 其他一些值得留意的细节

时间同步 :如果你的 go2rtc 之后还要做录像或者和 Frigate 联动,时间不准很致命。Docker 默认时区是 UTC,所以运行容器时一定记得设置 -e TZ=Asia/Shanghai 。我踩过一次亏,录像文件时间戳比实际慢 8 个小时,排查了半天才发现是时区问题。

CPU 与内存 :go2rtc 本身非常轻量,空闲时内存占用不到 30MB,CPU 几乎为 0。但一旦多路同时转码,CPU 会飙升。建议在 docker-compose 里限制资源:

deploy:
  resources:
    limits:
      cpus: "2.0"
      memory: 1G

这样即使某路流卡住或者转码任务失控,也不会拖垮宿主机上其他服务。

日志轮转 :时间久了容器日志会变得很大,Docker Desktop 默认把容器日志写到宿主机,体积膨胀可能会把磁盘挤爆。建议在 compose 里限制:

logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

这个配置让单个日志文件最大 10MB,最多保留 3 个,超过就滚动删除,对排查问题也够了。


5. 扩展玩法:把 go2rtc 的价值再放大一圈

5.1 把 go2rtc 变成“多协议网关”,而不是仅仅接摄像头

go2rtc 的输入源远不止 RTSP 摄像头。它可以拉取任意 HTTP/HTTPS 视频流,也可以处理 RTMP。举个例子,你想把某品牌无人机或运动相机的 RTMP 直播流接到统一监控台上看,go2rtc 里加一行配置就行:

streams:
  drone_fpv: rtmp://your-stream-server:1935/live/drone#video=copy#audio=copy

它还可以接入本地文件当作一个循环播放的“虚拟摄像头”,这对测试很有用:

streams:
  test_loop: /media/test.mp4

如果你在开发前端,需要一路稳定的测试视频源来调播放器,这个功能省去了很多装假流的麻烦。go2rtc 会把本地文件当作一路流,支持循环播放,WebRTC、HLS 输出方式都完全一样。

5.2 自己搞一个微型监控墙

go2rtc 的 Web UI 本身就能根据流名集中展示画面,但如果你想做一个更定制化的监控大屏,可以直接在一个 HTML 页面里通过 go2rtc 的 WebRTC API 连接多路流。好处是各路的延迟互不影响,也不会像 HLS 多路同时拉取那样把带宽吃满。我试过在 4K 带鱼屏上同时拉 16 路 WebRTC 摄像头流,CPU 占用只有个位数,非常流畅,这是 HLS 方案做不到的。

具体做法是,在页面上为每一路走一次 go2rtc 的 RTCPeerConnection 握手,拿到 MediaStream 后绑到不同的 <video> 元素。go2rtc 同时支持多个并发的 WebRTC 连接,所以再多路也扛得住。

5.3 高可用部署和备份配置

生产环境如果想要更高可用性,可以给 go2rtc 容器加健康检查:

healthcheck:
  test: ["CMD", "wget", "-q", "--spider", "http://localhost:1984/api/streams"]
  interval: 30s
  timeout: 5s
  retries: 3

这样 Docker 会定期探测 API 是否可用,如果连续 3 次失败就标记容器不健康。配合 restart: unless-stopped ,即使 go2rtc 进程由于某种原因僵死,Docker 也能在 30 秒内把它拉起来,监控中断时间不会太长。

备份配置的教训是我用真金白银换来的。有一次我调试完没做备份,第二天手滑把 /config 目录改了,结果所有摄像头流配置全丢了,只能重新一个个添加。从那以后,我每次改完 go2rtc.yaml,都会顺手备份一次:

cp /path/to/go2rtc/config/go2rtc.yaml /path/to/go2rtc/config/go2rtc.yaml.bak

如果整个目录都用 git 管理就更好了,加一行配置都能查到改动历史,比手动备份省心得多。


我自己现在家里的一套监控系统就是 go2rtc 做核心,总共接入了 12 路摄像头,5 路走 WebRTC 实时预览,7 路在 Frigate 里做 AI 检测,全部跑在一块 8 核小主机上,CPU 常年稳定在 15% 以下。这个组合最大的好处是“通吃”——不管摄像头牌子多杂、协议多乱,只要它能出 RTSP 流,go2rtc 就能把它纳入统一的管理体系,你的网页、手机、智能家居平台都能看到同一路画面。

如果你刚开始接触这套东西,我的建议是先从一条 docker run 命令开始跑通一个摄像头,再慢慢加配置、加流、加协议,最后过渡到 compose 编排。不要一上来就想着搞一个 20 路摄像头的完整系统,那只会让你被各种小问题淹没。按这篇的步骤来,你的第一路 WebRTC 摄像头流大概在半小时内就能看到。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐