SentryShot v0.3.11 Docker Compose 部署指南

在 Linux 服务器上用 Docker Compose 部署 SentryShot v0.3.11 的完整实测流程:首启配置生成、auth_none 引导建号、接入 RTSP 摄像头并完成录像与回放验证。

SentryShotDockerDocker ComposeRTSP2026/10/30

SentryShot v0.3.11 Docker Compose 部署指南

本文给出在 Linux 服务器上用 Docker Compose 部署 SentryShot v0.3.11 的完整流程:首次启动的配置文件生成、创建管理员账号、接入摄像头并用 ffmpeg 合成的测试流完成取流与录像验证。全程命令均在 Debian 12 服务器实测通过(2026-10-30)。

前置阅读:《SentryShot 介绍与选型指南》(含摄像头硬件前置要求)。

一、环境信息

  • 操作系统:Debian 12(宿主机),Docker Engine 27+ 与 Docker Compose v2(docker compose version 可用)。
  • SentryShot 版本:v0.3.11(2026-04-27 发布),软件包来源 codeberg.org/sentryshot/sentryshot:v0.3.11(Apache-2.0,主仓库在 Codeberg)。
  • 测试流组件:mediamtx 1.21.3(bluenviron/mediamtx:1.21.3-ffmpeg,自带 ffmpeg)。
  • 端口规划:SentryShot 服务端固定监听 2020,本文映射宿主机 8247;mediamtx 监听 8554,映射宿主机 8148。宿主端口需未被占用(ss -ltn | grep -E '8247|8148' 检查;本文实测时 8147 被宿主上其他进程占用,改用 8247,读者按空闲端口自行调整)。
  • 数据目录:/opt/sentryshot/stack/(配置与录像均在此目录下持久化)。
  • 真实摄像头场景只需第 1、3、4 步 + 第 5 步里把流地址换成摄像头 RTSP 地址;模拟流服务(mediamtx + ffmpeg 推流)可在验证完成后移除。

二、目录与文件准备

创建工作目录并写入三个文件:

mkdir -p /opt/sentryshot/stack/configs
mkdir -p /opt/sentryshot/stack/storage
cd /opt/sentryshot/stack

docker-compose.yml(栈内三个服务:SentryShot 本体、mediamtx 流媒体服务、ffmpeg 测试流推流服务):

name: sentryshot-0.3.11-stack

services:
  sentryshot:
    image: codeberg.org/sentryshot/sentryshot:v0.3.11
    container_name: sentryshot-0.3.11-web
    restart: unless-stopped
    ports:
      - "8247:2020"
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - ./configs:/app/configs
      - ./storage:/app/storage

  mediamtx:
    image: bluenviron/mediamtx:1.21.3-ffmpeg
    container_name: sentryshot-0.3.11-mediamtx
    restart: unless-stopped
    ports:
      - "8148:8554"
    volumes:
      - ./mediamtx.yml:/mediamtx.yml

  camera:
    image: bluenviron/mediamtx:1.21.3-ffmpeg
    container_name: sentryshot-0.3.11-camera
    restart: unless-stopped
    volumes:
      - /usr/share/fonts/truetype/dejavu:/fonts:ro
    entrypoint: >-
      ffmpeg -re -f lavfi -i testsrc2=size=1280x720:rate=25
      -vf "drawtext=fontfile=/fonts/DejaVuSansMono-Bold.ttf:text='CAM1 %{localtime}':x=16:y=16:fontsize=40:fontcolor=white:box=1:boxcolor=black@0.5"
      -c:v libx264 -preset veryfast -tune zerolatency -g 50 -pix_fmt yuv420p
      -f rtsp -rtsp_transport tcp rtsp://mediamtx:8554/cam1

networks:
  default:
    name: sentryshot-0.3.11-net
    ipam:
      config:
        - subnet: 10.89.67.0/24

说明:camera 服务用 ffmpeg 的 testsrc2 源合成带时间戳的 720p 测试画面并推到 mediamtx 的 cam1 路径,充当一台"模拟摄像头";drawtext 需要字体文件,故只读挂载了宿主机的 DejaVu 字体目录(若路径不同先 find /usr/share/fonts -name '*.ttf' 确认)。真实摄像头接入时把 sourcertsp.mainStream 换成摄像头地址即可,camera 与 mediamtx 两个服务可删除。

mediamtx.yml(接收推流的最小配置):

logLevel: info
paths:
  cam1:

三、首次启动与配置文件生成

SentryShot 首次启动有一个必须知道的行为:检测不到配置文件时,生成默认 sentryshot.toml 后立即退出,随后因为默认配置里两个认证插件都未启用而继续退出,需要手工编辑后再启动。完整过程如下。

第一次 docker compose up -d:

cd /opt/sentryshot/stack
docker compose up -d

实测回显:

 Network sentryshot-0.3.11-net Creating
 Network sentryshot-0.3.11-net Created
 Container sentryshot-0.3.11-web Creating
 Container sentryshot-0.3.11-mediamtx Creating
 Container sentryshot-0.3.11-camera Creating
 Container sentryshot-0.3.11-web Created
 Container sentryshot-0.3.11-mediamtx Created
 Container sentryshot-0.3.11-camera Created
 Container sentryshot-0.3.11-camera Starting
 Container sentryshot-0.3.11-web Starting
 Container sentryshot-0.3.11-mediamtx Starting
 Container sentryshot-0.3.11-camera Started
 Container sentryshot-0.3.11-web Started
 Container sentryshot-0.3.11-mediamtx Started

查看 SentryShot 服务日志,会看到它生成默认配置后退出:

docker logs sentryshot-0.3.11-web 2>&1 | head -3
Generating '/app/configs/sentryshot.toml' and exiting..

此时 configs/sentryshot.toml 已自动生成。由于默认配置中 auth_basic 与 auth_none 都是 enable = false,进程重启后会再次退出并提示启用认证插件(restart: unless-stopped 会不断重试):

docker logs sentryshot-0.3.11-web 2>&1 | tail -1
Please enable one of the authentication plugins in the generated sentryshot.toml file. See docs for more info

按官方安装文档指引,编辑 configs/sentryshot.toml,临时启用 auth_none(免认证,仅用于创建首个账号,创建后切回):

# No authentication.
[[plugin]]
name = "auth_none"
enable = true

再次启动并确认三个服务都在运行:

docker compose up -d
docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}'
NAME                         STATUS          PORTS
sentryshot-0.3.11-camera     Up 4 minutes
sentryshot-0.3.11-mediamtx   Up 4 minutes    0.0.0.0:8148->8554/tcp, [::]:8148->8554/tcp
sentryshot-0.3.11-web        Up 49 seconds   0.0.0.0:8247->2020/tcp, [::]:8247->2020/tcp
docker logs sentryshot-0.3.11-web --since 1m 2>&1 | head -5
[INFO] App: loading plugin auth_basic
[INFO] App: loading plugin motion
[INFO] App: loading plugin thumb_scale
[DEBUG] App: TZ=Asia/Shanghai
[INFO] App: Serving app on port 2020

看到 Serving app on port 2020 即服务就绪。configs/accounts.json 此时尚不存在,会在创建第一个账号时生成。

四、创建管理员账号(首次必做)

auth_none 已启用,此时无需认证即可调用建号接口。写请求(PUT)需要携带 CSRF token,token 从 GET /api/account/my-token 获取:

TOKEN=$(curl -s http://127.0.0.1:8247/api/account/my-token)
echo $TOKEN
btIFh54xNQF64esB071BAe452EgjOPBR

创建管理员账号(用户名 admin、密码 q1w2e3r4;账号 ID 为 16 位字符串,字符集为小写字母与数字 2-9,不含 0/1/i/l/o):

curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8247/api/account \
  -H "Content-Type: application/json" \
  -H "X-CSRF-TOKEN: $TOKEN" \
  -d '{"id":"a2b3c4d5e6f7g8h9","username":"admin","isAdmin":true,"plainPassword":"q1w2e3r4"}'

HTTP 201

PUT 成功返回 201、响应体为空。用列表接口确认账号已存在:

curl -s http://127.0.0.1:8247/api/accounts
{"a2b3c4d5e6f7g8h9":{"id":"a2b3c4d5e6f7g8h9","username":"admin","isAdmin":true}}

同时 configs/accounts.json 落盘,密码以 argon2id 哈希保存:

{
  "a2b3c4d5e6f7g8h9": {
    "id": "a2b3c4d5e6f7g8h9",
    "username": "admin",
    "password": "$argon2id$v=19$m=19456,t=2,p=1$yd8y1jQGF76oIp+7PP7Reg$p67eVK09hd2P4mNm5IBDmq11LBEiHFFLrS0DcMmzxFo",
    "isAdmin": true
  }
}

三类常见错误实测(便于排障对照):

# ID 长度不足 16 位
curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8247/api/account \
  -H "Content-Type: application/json" -H "X-CSRF-TOKEN: $TOKEN" \
  -d '{"id":"zzzz","username":"x","isAdmin":true,"plainPassword":"q1w2e3r4"}'
Failed to deserialize the JSON body into the target type: id: expected length 16, got 4 at line 1 column 12
HTTP 422
# ID 含字符集之外的字符('1' 不在允许字符集内)
curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8247/api/account \
  -H "Content-Type: application/json" -H "X-CSRF-TOKEN: $TOKEN" \
  -d '{"id":"a1b2c3d4e5f60718","username":"t","isAdmin":false,"plainPassword":"q1w2e3r4"}'
Failed to deserialize the JSON body into the target type: id: invalid character: '1' at line 1 column 24
HTTP 422
# Basic 认证开启后,写请求缺 X-CSRF-TOKEN 请求头
curl -s -w '\nHTTP %{http_code}\n' -u admin:q1w2e3r4 -X PUT http://127.0.0.1:8247/api/account \
  -H "Content-Type: application/json" \
  -d '{"id":"b2c3d4f5g6h7j8k9","username":"temp1","isAdmin":false,"plainPassword":"q1w2e3r4"}'
Invalid CSRF-token
HTTP 401

切回 Basic 认证:编辑 configs/sentryshot.toml,把 auth_none 的 enable 改回 false、auth_basic 改为 true,再次启动。验证未认证被拒、认证通过:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8247/live
curl -s -o /dev/null -w '%{http_code}\n' -u admin:q1w2e3r4 http://127.0.0.1:8247/live
401
200

浏览器访问 http://服务器IP:8247/live 弹出登录框,输入 admin/q1w2e3r4 即可进入直播页。

五、接入摄像头

模拟摄像头(本栈演练)

先确认测试流已在推流(在 camera 服务里用 ffprobe 探测 mediamtx):

docker compose exec camera ffprobe -v error -rtsp_transport tcp \
  -i rtsp://mediamtx:8554/cam1 \
  -show_entries stream=codec_name,width,height,avg_frame_rate -of default=noprint_wrappers=1
codec_name=h264
width=1280
height=720
avg_frame_rate=25/1

添加到 SentryShot

通过 API 添加名为 cam2 的摄像头,开启持续录像(接口细节见 《SentryShot v0.3.11 HTTP API 使用详解》):

TOKEN=$(curl -s -u admin:q1w2e3r4 http://127.0.0.1:8247/api/account/my-token)
curl -s -w '\nHTTP %{http_code}\n' -u admin:q1w2e3r4 -X PUT http://127.0.0.1:8247/api/monitor \
  -H "Content-Type: application/json" \
  -H "X-CSRF-TOKEN: $TOKEN" \
  -d '{"id":"cam2","name":"cam2","enable":true,"alwaysRecord":true,"videoLength":1,"source":"rtsp","sourcertsp":{"protocol":"tcp","mainStream":"rtsp://mediamtx:8554/cam1"}}'

HTTP 200

说明:PUT 同一 ID 时是全量覆盖(upsert),返回 201(新建)或 200(更新)都属正常。请求体里未携带的字段(如 motion 移动侦测段)会被清空,需要时应在请求体中一并给出。

真实摄像头把 mainStream 换成如 rtsp://用户名:密码@192.168.1.64:554/stream1 即可;协议建议 tcp。

服务端日志出现取流成功标志:

[DEBUG] cam2: Monitor: (main) rtsp source: using h264 video stream
[INFO] cam2: Monitor: recorder: starting recording: 2026-10-30_01-26-03_cam2

六、验证录像与回放

等 1~2 分钟(videoLength: 1 表示按 1 分钟粒度入库,参数语义见 《SentryShot v0.3.11 配置参数详解》),检查存储目录与录像查询:

ls /opt/sentryshot/stack/storage/recordings/2026/10/30/cam2/ | head -8

输出节选(实测首尾,共 115 个文件):

2026-10-30_00-26-08_cam2.jpeg
2026-10-30_00-26-08_cam2.json
2026-10-30_00-26-08_cam2.mdat
2026-10-30_00-26-08_cam2.meta
2026-10-30_00-33-12_cam2.jpeg
2026-10-30_00-33-12_cam2.json
2026-10-30_00-33-12_cam2.mdat
2026-10-30_00-33-12_cam2.meta

每个录像段落四个文件:.mdat 视频数据、.meta 元数据、.json 时间信息、.jpeg 缩略图。

查询录像段落(recording-id 传一个"未来"的时间串表示从头查,include-data 必传):

curl -s -u admin:q1w2e3r4 \
  "http://127.0.0.1:8247/api/recording/query?recording-id=2099-01-01_00-00-00_x&limit=3&reverse=false&include-data=true"
[{"state":"active","id":"2026-10-30_01-08-23_cam2","data":{"start":1793322503575455555,"end":1793322514575455555,"events":[]}},{"state":"finalized","id":"2026-10-30_01-07-21_cam2","data":{"start":1793322441575455555,"end":1793322503575455555,"events":[]}},{"state":"finalized","id":"2026-10-30_01-06-19_cam2","data":{"start":1793322379575455555,"end":1793322441575455555,"events":[]}}]

回放验证(下载一段完整录像 MP4,/api/recording/video/<录像ID>):

curl -s -u admin:q1w2e3r4 -o /dev/null -w '%{http_code} %{size_download}\n' \
  "http://127.0.0.1:8247/api/recording/video/2026-10-30_00-41-06_cam2"
200 32354736

缩略图接口同样可用:

curl -s -u admin:q1w2e3r4 -o /dev/null -w '%{http_code} %{size_download}\n' \
  "http://127.0.0.1:8247/api/recording/thumbnail/2026-10-30_00-41-06_cam2"
200 52694

浏览器侧:/live 页可看到摄像头实时画面,/recordings 页按日期浏览录像段落并在线播放。至此部署闭环完成。

七、常用运维命令

cd /opt/sentryshot/stack
docker compose ps                       # 查看三个服务状态
docker logs sentryshot-0.3.11-web --since 10m  # 最近 10 分钟服务日志
docker compose restart sentryshot       # 修改 toml 配置后重启生效
docker compose down                     # 停止并移除编排(configs/ 与 storage/ 数据保留)
docker compose up -d                    # 重新拉起

八、排障实录

现象原因与处理
首次 up 后服务反复重启首次启动生成默认配置即退出;默认配置两个认证插件均为 enable = false,按第三章手工启用 auth_none(或 auth_basic)后再启动
PUT /api/account 返回 401,响应体 Invalid CSRF-token建号、改配置类写请求必须带 X-CSRF-TOKEN 请求头,token 从 GET /api/account/my-token 获取
建号返回 422账号 ID 必须 16 位、字符集为小写字母与数字 2-9(0/1/i/l/o 不可用);报错原文见第四章负样本
摄像头添加后无画面服务端日志无 using h264 video stream 行时,检查摄像头是否输出 H.264、地址端口是否可达;SentryShot 仅接受 H.264 视频轨
修改摄像头配置后不生效PUT /api/monitor 保存后需调用 POST /api/monitor/restart?id=<id>(同样带 CSRF token)才会按新配置重建取流与录制
修改摄像头配置后移动侦测丢失PUT /api/monitor 是全量覆盖,请求体漏掉 motion 段会把已有移动侦测配置清空,需要原样带回
ffmpeg drawtext 启动失败字体路径不存在,确认宿主机字体文件并保持只读挂载路径一致
宿主端口映射后无法访问检查宿主端口是否被其他进程占用(`ss -ltnp

九、延伸阅读