Atsumeru Docker Compose 部署指南

Atsumeru 官方 Docker 镜像(atsumerudev/atsumeru:latest,实测为 v1.1)的 Compose 全流程部署实录:镜像选型与版本落差、五卷挂载与 31337 端口、JAVA_OPT_XMX 堆限与 ps 验证、首次启动随机 Admin 口令提取与改密、CBZ 测试库导入全回显,含 401/404 排障实录(2026-11-01 实测)。

AtsumeruAtsumeru漫画媒体服务器NAS 自托管Docker Compose2026/11/01

Atsumeru Docker Compose 部署指南

目标:用 Docker Compose 把 Atsumeru 官方镜像从零拉起——镜像选型(含官方镜像落后 GitHub 稳定版的版本落差说明)、五个数据卷与 31337 端口规划、JVM 堆限注入与生效验证、首次启动随机 Admin 口令提取与改密、注册导入目录并实测导入一个 CBZ 测试漫画,全部命令与回显为 2026-11-01 实测。产品定位与选型背景见 《Atsumeru 介绍与选型指南》。

一、环境信息

  • 宿主机:Ubuntu 26.04.1 LTS,x86_64
  • Docker:29.9.2;Docker Compose:v5.6.1
  • 软件版本:Atsumeru v1.1(官方 GitHub 最新稳定版为 2.2,官方镜像未跟进,选型说明见二;版本线溯源见 《Atsumeru 介绍与选型指南》 第二章)
  • 镜像:atsumerudev/atsumeru:latest(镜像 ID 16ee7196726a,本地大小 485MB,内置 Temurin OpenJDK 17.0.13+11 JRE)
  • 项目名:atsumeru-11-stack(compose 显式 name:,防多项目互删)
  • 网络:自定义 bridge atsumeru-11-stack_atsumeru-net,子网 10.89.57.0/24,服务固定地址 10.89.57.10
  • 端口:宿主 8138 → 服务 31337(Atsumeru 默认 API 端口)
  • 数据目录(宿主侧,均 bind 挂载):/opt/data/quzuo/tmp/atsumeru/{library,config,database,cache,logs}
  • JVM 堆限:JAVA_OPT_XMX=-Xms256m -Xmx512m(注入位置与验证见 4.4)
  • 演示账号:Admin / q1w2e3r4(随机初始口令经 4.3 改密而来;普通用户 root / q1w2e3r4)

二、镜像选型说明

2.1 官方镜像与 tag 策略

Docker Hub 仓库 atsumerudev/atsumeru 实测仅有 4 个 tag(/v2/tags API,2026-11-01 查询):

tag最后推送时间对应程序版本
latest2025-01-151.1
1.12025-01-151.1
1.0.32023-12-211.0.3
1.0.22023-08-231.0.2

关键事实:官方镜像线停在 1.1。GitHub Releases 的 1.2/1.3/2.0/2.1/2.2(最新稳定 2.2,2026-07-08)目前只提供 Atsumeru-2.2.jar 构件,没有对应镜像。因此:

  • 走 Compose/镜像 路线 = 得到 1.x 形态:无 Web 管理界面、无 Swagger-UI,管理靠 REST API + 桌面端 Atsumeru Manager;
  • 走 jar 路线 = 可部署 2.2,获得 WebUI(2.0 起)与 OpenAPI 文档(1.2 起),但需要自备 Java 17+ 运行时并自行做 systemd 托管。

⚠️ 注意:生产建议用 1.1 版本号 tag 替代 latest 锁定版本,避免未来官方突然推新 latest 造成不可控升级。本文实测即 latest(当前等于 1.1)。

2.2 架构支持

官方 docker.html 明示镜像支持 linux/amd64、linux/arm64、linux/arm/v7(树莓派可跑),但 ARM 模式下仅支持读取 ZIP/CBZ 压缩包,PDF/DJVU 导入不可用。本文环境为 amd64,不受该限制。

三、前置条件

  1. 宿主机已装 Docker 20+ 与 Compose v2 插件(本机 29.9.2 / v5.6.1);
  2. 宿主端口未占用:31337 或计划映射端口(本机取 8138)空闲——ss -ltn | grep 8138 部署前无输出;
  3. 已规划数据目录并预建五个子目录(library/config/database/cache/logs),目录属主对容器内 root 可写;
  4. 若走自定义 bridge 网络,确认目标子网(本机 10.89.57.0/24)未被占用:docker network ls 无同网段冲突。

四、部署步骤

4.1 编写 compose 文件

# /opt/data/quzuo/tmp/atsumeru/compose/docker-compose.yml(实测全文)
name: atsumeru-11-stack
services:
  atsumeru:
    image: atsumerudev/atsumeru:latest
    container_name: atsumeru-11-web
    ports:
      - '8138:31337'
    environment:
      - JAVA_OPT_XMX=-Xms256m -Xmx512m
    volumes:
      - '/opt/data/quzuo/tmp/atsumeru/library:/library'
      - '/opt/data/quzuo/tmp/atsumeru/config:/app/config'
      - '/opt/data/quzuo/tmp/atsumeru/database:/app/database'
      - '/opt/data/quzuo/tmp/atsumeru/cache:/app/cache'
      - '/opt/data/quzuo/tmp/atsumeru/logs:/app/logs'
    networks:
      atsumeru-net:
        ipv4_address: 10.89.57.10
    restart: unless-stopped

networks:
  atsumeru-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.57.0/24

逐项说明:name: 显式项目名是批次硬规(防 down 误删他组件栈);ports 把默认 API 端口 31337 映射到宿主 8138;五个卷对应官方 docker.html 的挂载清单(library 书库、/app/config 配置、/app/database 双 SQLite 库、/app/cache 封面缓存、/app/logs 日志);JAVA_OPT_XMX 是镜像入口脚本识别的堆参数变量(见 4.4);restart: unless-stopped 与官方示例一致。

4.2 启动并验证服务状态

docker compose -p atsumeru-11-stack -f /opt/data/quzuo/tmp/atsumeru/compose/docker-compose.yml up -d

启动后状态实测:

$ docker compose -p atsumeru-11-stack ps
NAME              IMAGE                         COMMAND                  SERVICE    CREATED          STATUS                    PORTS
atsumeru-11-web   atsumerudev/atsumeru:latest   "sh -c 'java -Dserve…"   atsumeru   26 minutes ago   Up 26 minutes (healthy)   0.0.0.0:8138->31337/tcp, [::]:8138->31337/tcp

镜像自带健康检查,STATUS 显示 (healthy) 即为就绪。

4.3 首次启动:提取随机 Admin 口令

启动日志(节选,实测首尾):

     _   _
    / \ | |_ ___ _   _ _ __ ___   ___ _ __ _   _
   / _ \| __/ __| | | | '_ ` _ \ / _ \ '__| | | |
  / ___ \ |_\__ \ |_| | | | | | |  __/ |  | |_| |
 /_/   \_\__|___/\__,_|_| |_| |_|\___|_|   \__,_|

Version:  (v1.1)

2026-11-01T10:56:27.081Z  INFO 7 --- [Atsumeru] [           main] xyz.atsumeru.web.AtsumeruApplication     : Starting AtsumeruApplication v1.1 using Java 17.0.13 with PID 7 (/app/Atsumeru.jar started by root in /app)
2026-11-01T10:56:27.083Z  INFO 7 --- [Atsumeru] [           main] xyz.atsumeru.web.AtsumeruApplication     : The following 1 profile is active: "production"
2026-11-01T10:56:27.772Z  INFO 7 --- [Atsumeru] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 31337 (http)
...
2026-11-01T10:56:28.022Z  INFO 7 --- [Atsumeru] [           main] BooksDaoManager                          : Connected to books database!
2026-11-01T10:56:28.026Z  WARN 7 --- [Atsumeru] [           main] ImportFolders                            : Unable to load folders.properties... Maybe there is no folders yet?
2026-11-01T10:56:28.027Z  WARN 7 --- [Atsumeru] [           main] Settings                                 : Unable to load atsumeru.properties. A new one will be created...
...
2026-11-01T10:56:28.379Z  INFO 7 --- [Atsumeru] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 31337 (http) with context path '/'
2026-11-01T10:56:28.389Z  INFO 7 --- [Atsumeru] [           main] xyz.atsumeru.web.AtsumeruApplication     : Started AtsumeruApplication in 1.553 seconds (process running for 1.825)
2026-11-01T10:56:28.390Z  WARN 7 --- [Atsumeru] [           main] UsersRepository                          : [Admin] user not found. Creating one...
2026-11-01T10:56:28.446Z  INFO 7 --- [Atsumeru] [           main] UsersRepository                          : [Admin] user created with password = (63,_BlEAp42

两条 WARN 是首次启动的正常现象(尚无配置/导入目录)。管理员口令只打印一次,随时可回查:

docker logs atsumeru-11-web 2>&1 | grep -i password
2026-11-01T10:56:28.446Z  INFO 7 --- [Atsumeru] [           main] UsersRepository                          : [Admin] user created with password = (63,_BlEAp42

⚠️ 排障实录(登录端点 401):Atsumeru 2.x 客户端习惯先调 POST /auth/login 换 token,但 1.1 没有该端点。实测:

$ curl -i -X POST http://localhost:8138/auth/login -H 'Content-Type: application/json' -d '{"login":"Admin","password":"(63,_BlEAp42"}'
HTTP/1.1 401
WWW-Authenticate: Basic realm="Realm"
Content-Length: 0

定位:读 1.1 源码 WebSecurityConfiguration.java(jsDelivr 镜像取证),确认 1.1 仅启用 httpBasic(),无 login/logout/refresh 控制器。结论:1.1 直接用 HTTP Basic 认证,curl 加 -u '用户名:密码' 即可,无需登录步骤。

4.4 JVM 堆限注入与生效验证

镜像入口命令实测为:

sh -c "java -Dserver.port=31337 ${JAVA_OPT_XMX} -jar Atsumeru.jar"

即堆参数经环境变量 JAVA_OPT_XMX 注入(compose 的 environment: 处修改,见 4.1);jar 直跑形态则写在 java -Xms256m -Xmx512m -jar ... 命令行。批次规范堆限 256–512MB,本文取 -Xms256m -Xmx512m。

验证一(首选 jps -v,官方镜像为 JRE 无此命令,实录如下):

$ docker exec atsumeru-11-web jps -v
OCI runtime exec failed: exec failed: unable to start container process: exec: "jps": executable file not found in $PATH

⚠️ 排障实录:atsumerudev/atsumeru 镜像基于 Temurin JRE(/opt/java/openjdk/bin 下无 jps/jcmd 等 JDK 工具),jps -v 在镜像内不可用。改用进程命令行验证。

验证二(实测生效值):

docker exec atsumeru-11-web ps -ef | grep '[j]ava'
root           7       1  0 10:56 ?        00:00:07 java -Dserver.port=31337 -Xms256m -Xmx512m -jar Atsumeru.jar

-Xms256m -Xmx512m 已按预期进入 JVM 命令行;docker inspect 亦可见 Env 中 JAVA_OPT_XMX=-Xms256m -Xmx512m。jar 形态在真机上可直接 jps -v(JDK 自带)看到同样的 -Xms256m -Xmx512m。

4.5 首次配置:改密与连通性验证

Atsumeru 1.x 无 Web 管理界面(浏览器打开 http://<宿主>:8138/ 只会得到 401),首次配置走 API。先验证 Basic 连通:

curl -s -u 'Admin:(63,_BlEAp42' -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8138/api/server/ping
HTTP 200

⚠️ 排障实录(端点 404):最初按直觉请求 http://localhost:8138/server/info,返回:

{"type":"problemDetail.type.org.springframework.web.servlet.resource.NoResourceFoundException","title":"problemDetail.title.org.springframework.web.servlet.resource.NoResourceFoundException","status":404,"detail":"problemDetail.org.springframework.web.servlet.resource.NoResourceFoundException","instance":"/server/info"}

定位:1.1 全部 REST 控制器挂载在 /api 前缀下(源码 ServerApiController.ROOT_ENDPOINT = "/api/server",ImporterApiController 等为 /api/v1/...)。结论:正确路径是 /api/server/info。

把随机口令改成演示口令 q1w2e3r4(用户名保持默认 Admin):

curl -s -X PATCH -u 'Admin:(63,_BlEAp42' -H 'Content-Type: application/json' \
  -d '{"id":1,"user_name":"Admin","password":"q1w2e3r4","roles":["ADMIN"],"authorities":["ROLE_ADMIN"]}' \
  http://localhost:8138/api/v1/users/update
{"code":201,"message":"User successfully created"}
curl -s -u 'Admin:q1w2e3r4' -o /dev/null -w 'new-pass ping: HTTP %{http_code}\n' http://localhost:8138/api/server/ping
curl -s -u 'Admin:(63,_BlEAp42' -o /dev/null -w 'old-pass ping: HTTP %{http_code}\n' http://localhost:8138/api/server/ping
new-pass ping: HTTP 200
old-pass ping: HTTP 401

新口令生效、旧口令失效。服务端信息确认版本线:

curl -s -u 'Admin:q1w2e3r4' http://localhost:8138/api/server/info
{"name":"Atsumeru","version":"1.1","version_name":"Bohrium","has_password":true,"debug_mode":false,"stats":{"total_series":0,"total_archives":0,"total_chapters":0,"total_categories":0}}

4.6 准备测试书库并注册导入目录

在挂载的 library 卷里造一个最小 CBZ(zip 内含 3 页 PNG + ComicInfo.xml 边车元数据):

mkdir -p /opt/data/quzuo/tmp/atsumeru/library/manga/AtsumeruTestSeries
cd /opt/data/quzuo/tmp/atsumeru/library/manga/AtsumeruTestSeries
python3 - <<'EOF'
import zlib, struct, zipfile
def png(w,h,rgb):
    raw=b''.join(b'\x00'+bytes(rgb)*w for _ in range(h))
    def chunk(t,d):
        c=t+d
        return struct.pack('>I',len(d))+c+struct.pack('>I',zlib.crc32(c)&0xffffffff)
    ihdr=struct.pack('>IIBBBBB',w,h,8,2,0,0,0)
    return b'\x89PNG\r\n\x1a\n'+chunk(b'IHDR',ihdr)+chunk(b'IDAT',zlib.compress(raw))+chunk(b'IEND',b'')
comicinfo='''<?xml version="1.0" encoding="utf-8"?>
<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ComicInfo.xsd">
  <Series>AtsumeruTestSeries</Series>
  <Number>1</Number>
  <Title>Volume 1</Title>
  <Writer>quzuo</Writer>
  <Genre>Action</Genre>
  <PageCount>3</PageCount>
</ComicInfo>'''
with zipfile.ZipFile('AtsumeruTestSeries v001.cbz','w') as z:
    for i,c in enumerate([(240,60,60),(60,240,60),(60,60,240)],1):
        z.writestr(f'{i:03d}.png', png(64,96,c))
    z.writestr('ComicInfo.xml', comicinfo)
EOF
ls -la
-rw-r--r-- 1 root root 1384 Nov  1 19:17 'AtsumeruTestSeries v001.cbz'

注册导入目录(路径是服务视角路径,即容器内挂载点 /library/manga,不是宿主路径):

curl -s -X POST -u 'Admin:q1w2e3r4' -H 'Content-Type: application/json' \
  -d '{"path":"/library/manga","singles":false}' \
  http://localhost:8138/api/v1/importer/add
{"code":200,"message":"Folder added"}
curl -s -u 'Admin:q1w2e3r4' http://localhost:8138/api/v1/importer/list
[{"hash":"63fe8498b2a1b0edec7c2efdc8fd3296","path":"/library/manga","singles":false,"singles_in_root":false,"singles_if_in_root_with_folders":false,"ignore_volume_numbers_detection":false,"series_count":0,"singles_count":0,"archives_count":0,"chapters_count":0}]

4.7 触发扫描并验证入库

curl -s -u 'Admin:q1w2e3r4' http://localhost:8138/api/v1/importer/scan
{"code":200,"message":"Scan started"}
curl -s -u 'Admin:q1w2e3r4' http://localhost:8138/api/v1/importer/status
{"import_active":false,"last_start_time":1793531877754,"running_ms":0,"imported":0,"total":0,"percent":0.0}

(status 的 imported=0 是因为注册目录时文件监听器已完成首轮导入,scan 无增量可导。)验证书库:

curl -s -u 'Admin:q1w2e3r4' http://localhost:8138/api/v1/books
[{"id":"atsumeru-serieb71204461360651e64a359ad591269ac","title":"Volume 1","is_single":false,"folder":"/library/manga/AtsumeruTestSeries","cover":"atsumeru36fae69422e431620cadd1b555d248c9?t=1793531877744","authors":["quzuo"],"genres":["Action"],"content_type":"UNKNOWN","is_mature":false,"is_adult":false,"pagesCount":3,"status":"ONGOING","translation_status":"UNKNOWN","plot_type":"UNKNOWN","censorship":"UNKNOWN","color":"UNKNOWN","rating":0,"created_at":1793531877744,"updated_at":1793531877744}]

ComicInfo.xml 的边车元数据(作者 quzuo、流派 Action、页数 3)已被自动解析入库。服务器统计同步更新:

curl -s -u 'Admin:q1w2e3r4' http://localhost:8138/api/server/info
{"name":"Atsumeru","version":"1.1","version_name":"Bohrium","has_password":true,"debug_mode":false,"stats":{"total_series":1,"total_archives":1,"total_chapters":0,"total_categories":1}}

取第一页图像验证阅读链路(返回 64×96 PNG):

curl -s -u 'Admin:q1w2e3r4' \
  http://localhost:8138/api/v1/books/atsumeru36fae69422e431620cadd1b555d248c9/page/1 -o page1.png -w 'HTTP %{http_code} %{content_type} %{size_download}B\n'
file page1.png
HTTP 200 image/png 222B
page1.png: PNG image data, 64 x 96, 8-bit/color RGB, non-interlaced

部署至此端到端可用。

五、数据持久化说明

五个 bind 目录与容器内路径的对应关系(导入后实测宿主侧内容):

宿主目录容器路径内容(实测)
tmp/atsumeru/library/library书库原文件(本例 manga/AtsumeruTestSeries/*.cbz)
tmp/atsumeru/config/app/configatsumeru.properties(服务设置)、folders.properties(导入目录注册表)
tmp/atsumeru/database/app/databaselibrary.db(+-shm/-wal) 书库库、users.db 用户库(SQLite)
tmp/atsumeru/cache/app/cache封面缓存(清空后由 clear_cover_cache 重建)
tmp/atsumeru/logs/app/logsimport.log、requests.log(含 .lck)

备份要点:停服后整目录打包 config/database/logs 即完成元数据备份(SQLite 的 -wal 文件必须一起拷);library 是原始内容、cache 可再生,按需取舍。

六、环境变量表

变量实测值作用依据
JAVA_OPT_XMX-Xms256m -Xmx512m拼入入口脚本 java ... ${JAVA_OPT_XMX} -jar Atsumeru.jar 的堆参数docker inspect Entrypoint + ps -ef 验证(4.4)
JAVA_HOME/opt/java/openjdk镜像内置 Temurin JRE 路径(镜像自带,勿改)docker inspect Env
JAVA_VERSIONjdk-17.0.13+11内置运行时版本(信息性)同上

服务端口无环境变量可改:入口脚本硬编码 -Dserver.port=31337,换端口靠 compose 的 ports 宿主侧映射(本例 8138)。

七、配置文件挂载与说明

首次改设置/注册目录后,/app/config 落盘两个文件(宿主侧 cat 实测):

# config/atsumeru.properties
#Auto save properties: Sun Nov 01 11:18:52 UTC 2026
#Sun Nov 01 11:18:52 UTC 2026
allow_loading_list_with_volumes=true
allow_loading_list_with_chapters=false
disable_chapters=true
disable_file_watcher=false
disable_watch_for_modified_files=false
disable_request_logging_into_console=true
# config/folders.properties
[{"hash":"63fe8498b2a1b0edec7c2efdc8fd3296","path":"/library/manga","singles":false,"singles_in_root":false,"singles_if_in_root_with_folders":false,"ignore_volume_numbers_detection":false,"series_count":0,"singles_count":0,"archives_count":0,"chapters_count":0}]

atsumeru.properties 六个键的含义与取值建议逐项见本专栏配置详解篇;folders.properties 是导入目录注册表(JSON 数组),不建议手改,由 /api/v1/importer/add|remove 维护,手改后需重启服务生效。

八、等价 docker run

官方 docker.html 给出的等价单命令(路径与端口按本文环境替换,其余与 compose 版等价):

docker run -d \
  --name=atsumeru \
  -p 8138:31337 \
  -e 'JAVA_OPT_XMX=-Xms256m -Xmx512m' \
  -v /opt/data/quzuo/tmp/atsumeru/library:/library \
  -v /opt/data/quzuo/tmp/atsumeru/config:/app/config \
  -v /opt/data/quzuo/tmp/atsumeru/database:/app/database \
  -v /opt/data/quzuo/tmp/atsumeru/cache:/app/cache \
  -v /opt/data/quzuo/tmp/atsumeru/logs:/app/logs \
  --restart unless-stopped \
  atsumerudev/atsumeru:latest

差异说明:compose 版额外固定了项目名、自定义网络与容器 IP(便于多栈隔离与复查);官方原版端口写 31337:31337,本环境按端口规划取 8138。

九、常用运维命令

# 查看服务状态与健康
docker compose -p atsumeru-11-stack ps

# 跟踪日志(导入/请求排障)
docker logs -f atsumeru-11-web

# 重启服务(改手编配置后)
docker compose -p atsumeru-11-stack restart

# 升级路线 A:官方若发布新镜像,改 image tag 后重拉
docker pull atsumerudev/atsumeru:latest && docker compose -p atsumeru-11-stack up -d

# 升级路线 B:当前官方最新稳定 2.2 无镜像,需 jar 形态(Java 17+ 主机上
# java -Xms256m -Xmx512m -jar Atsumeru-2.2.jar,数据目录同构可直接迁移)

# 停栈清理(只影响本项目)
docker compose -p atsumeru-11-stack down

十、常见问题

Q1:浏览器打开管理页 401/空白? 1.1 无 WebUI,根路径全部要求 Basic 认证。管理走 API(本专栏 API 篇)或桌面端 Atsumeru Manager;要 WebUI 需 jar 部署 2.x(见二)。

Q2:Admin 密码忘了? 官方 jar.html 给出的恢复法:停服,删除 database/users.db,重启——服务端重建随机口令 Admin(打印在日志),代价是全部用户与权限丢失,多用户环境慎用;改密用 PATCH /api/v1/users/update(见 4.5)。

Q3:jps -v 报 executable file not found? 官方镜像是 JRE 不含 JDK 工具,用 docker exec <name> ps -ef | grep '[j]ava' 验证堆参数(见 4.4 排障实录)。

Q4:导入后 status 显示 imported=0? 文件监听器在目录注册时已首轮导入,scan 只处理增量,属正常(见 4.7)。

Q5:树莓派上 PDF 漫画不入库? ARM 镜像仅支持 ZIP/CBZ 读取(官方 docker.html 明示),PDF/DJVU 需 amd64 环境。

十一、参考链接

Atsumeru Docker Compose 部署指南 - Quzuo