Apache Subversion Docker Compose 部署指南

用 Docker Compose 从零部署 Apache Subversion 集中式版本控制服务,基于 ubuntu:22.04 自建含 mod_dav_svn 的服务镜像,覆盖镜像选型对比、Dockerfile 与 entrypoint 编写、compose 编排启动验证、宿主机端口发布、数据卷持久化与重建验证、环境变量速查、等价 docker run 与运维排障,全程命令回显实测。

Apache SubversionApache Subversion2026/10/12

Apache Subversion Docker Compose 部署指南

目标:用 Docker Compose 在一台主机上从零部署 Apache Subversion 服务(Apache httpd + mod_dav_svn 提供 http:// 访问),覆盖自建镜像、compose 编排、端口发布、数据卷持久化、重启验证与运维排障。以下命令与回显均在 Ubuntu 22.04.5 LTS + Docker 环境逐步实测。

一、环境信息

  • 宿主操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • Docker / Compose:Docker Engine + docker compose v2 插件
  • 自建镜像:svn-server:1.14(基于 ubuntu:22.04)
  • 镜像内组件:Apache Subversion 1.14.1-3ubuntu0.22.04.1、Apache 2.4.52、libapache2-mod-svn 1.14.1
  • 编排目录:/opt/svn-compose(compose 项目名 subversion-compose)
  • 端口发布:宿主机 18080 → 容器内 80
  • 数据卷:./svn-data:/srv/svn(bind mount)
  • 访问账号:admin / root,密码 q1w2e3r4

二、镜像选型说明

候选结论理由
Docker Hub 官方 subversion 镜像不存在Apache 官方从未发布 Subversion 官方镜像
社区镜像(如 elleflorio/svn-server)不采用非官方维护、版本与更新节奏不可控
自建镜像(ubuntu:22.04 + apt 安装 subversion/apache2/libapache2-mod-svn)采用版本明确(1.14.1)、与裸机安装一致、apt 安全更新可跟进、配置文件完全自持

💡 说明:Subversion 服务端本身只有几十 MB 的 apt 包体,自建镜像构建只需一分钟出头,且 dav_svn.conf、htpasswd、authz 等配置全部掌握在自己手里,运维上最可控。

三、前置条件

  • 宿主机已安装 Docker Engine 与 docker compose v2 插件(docker compose version 可执行)。
  • 宿主机 18080 端口未被占用(占用时的处理见 11.1)。
  • 部署目录可写(示例用 /opt/svn-compose)。
  • 不强制要求宿主机安装 svn 客户端:自建镜像内自带 svn 命令行,可直接 docker compose exec 进入验证。

四、部署步骤

4.1 创建目录结构

mkdir -p /opt/svn-compose/conf
cd /opt/svn-compose

最终目录结构(svn-data 由首启自动生成):

/opt/svn-compose/
├── conf/
│   └── dav_svn.conf
├── svn-data/            # 首次启动后自动出现
│   └── repo1/
├── docker-compose.yml
├── Dockerfile
└── entrypoint.sh

4.2 编写 Dockerfile

FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        subversion apache2 libapache2-mod-svn curl && \
    a2enmod dav dav_svn authz_svn && \
    rm -rf /var/lib/apt/lists/*

COPY conf/dav_svn.conf /etc/apache2/mods-enabled/dav_svn.conf
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh && \
    echo "ServerName svn-server" > /etc/apache2/conf-available/servername.conf && \
    a2enconf servername

EXPOSE 80

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

4.3 编写 dav_svn.conf

conf/dav_svn.conf:

<Location /svn>
    DAV svn
    SVNParentPath /srv/svn
    AuthType Basic
    AuthName "Subversion Repository"
    AuthUserFile /etc/svn/svn.htpasswd
    AuthzSVNAccessFile /etc/svn/authz
    Require valid-user
</Location>

4.4 编写 entrypoint.sh

entrypoint.sh(首次启动自动建库、建账号,随后前台启动 Apache):

#!/bin/bash
set -e

SVN_ROOT=/srv/svn
HTPASSWD_FILE=/etc/svn/svn.htpasswd
AUTHZ_FILE=/etc/svn/authz
SVN_USER=${SVN_USER:-admin}
SVN_PASS=${SVN_PASS:-q1w2e3r4}
REPO_NAME=${REPO_NAME:-repo1}

mkdir -p /etc/svn

if [ ! -f "$HTPASSWD_FILE" ]; then
  htpasswd -cb "$HTPASSWD_FILE" "$SVN_USER" "$SVN_PASS"
fi

if [ ! -f "$AUTHZ_FILE" ]; then
  cat > "$AUTHZ_FILE" <<EOF
[groups]
dev = ${SVN_USER}

[/]
@dev = rw
EOF
fi

if [ ! -d "$SVN_ROOT/$REPO_NAME" ]; then
  svnadmin create "$SVN_ROOT/$REPO_NAME"
fi

chown -R www-data:www-data "$SVN_ROOT"

exec apachectl -D FOREGROUND

4.5 编写 docker-compose.yml

services:
  svn:
    build: .
    image: svn-server:1.14
    container_name: svn-server
    ports:
      - "18080:80"
    environment:
      SVN_USER: admin
      SVN_PASS: q1w2e3r4
      REPO_NAME: repo1
    volumes:
      - ./svn-data:/srv/svn
    restart: unless-stopped

4.6 构建并启动

docker compose up -d --build

真实回显(节选):

 Network subversion-compose_default Created
 Container svn-server Created
 Container svn-server Starting
 Container svn-server Started

查看运行状态:

$ docker compose ps
NAME         IMAGE             COMMAND                   SERVICE   CREATED         STATUS         PORTS
svn-server   svn-server:1.14   "/usr/local/bin/entr…"   svn       5 seconds ago   Up 4 seconds   0.0.0.0:18080->80/tcp, [::]:18080->80/tcp

4.7 访问验证

匿名请求被 Basic 认证拦截(预期 401),携带账号后返回 mod_dav_svn 生成的版本库索引:

$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:18080/svn/repo1/
401

$ curl -s -u admin:q1w2e3r4 http://127.0.0.1:18080/svn/repo1/ | head -4
<html><head><title>repo1 - Revision 0: /</title></head>
<body>
 <h2>repo1 - Revision 0: /</h2>
 <ul>

💡 宿主机未装 svn 客户端时,用镜像内自带的 svn 命令验证完整读写链路:docker compose exec svn svn log http://127.0.0.1/svn/repo1 --username admin --password q1w2e3r4 --non-interactive。

容器内导入、检出、提交实测回显:

# svn import -m "initial import" proj http://127.0.0.1/svn/repo1/project-trunk --username admin --password q1w2e3r4 --non-interactive
Adding         proj/trunk
Adding         proj/trunk/README.md
Committing transaction...
Committed revision 1.

# svn checkout http://127.0.0.1/svn/repo1/project-trunk/trunk wc-c --username admin --password q1w2e3r4 --non-interactive
A    wc-c/README.md
Checked out revision 1.

# cd wc-c && echo "commit from client" >> README.md
# svn commit -m "first commit on compose" --username admin --password q1w2e3r4 --non-interactive
Sending        README.md
Transmitting file data .done
Committing transaction...
Committed revision 2.

五、数据持久化说明

  • 版本库数据通过 bind mount ./svn-data:/srv/svn 落到宿主机,容器销毁不丢数据。
  • 首次启动后宿主机目录内容:
$ ls -la svn-data/
drwxr-xr-x 3 www-data www-data 4096 10月 12 08:59 .
drwxr-xr-x 4 root     root     4096 10月 12 08:59 ..
drwxr-xr-x 6 www-data www-data 4096 10月 12 08:59 repo1
  • docker compose down + docker compose up -d 重建服务后提交历史完整保留(r1、r2 均在):
$ docker compose down
 Network subversion-compose_default Removed
$ docker compose up -d
 Container svn-server Started
$ docker exec svn-server svn log http://127.0.0.1/svn/repo1/project-trunk --username admin --password q1w2e3r4 --non-interactive
------------------------------------------------------------------------
r2 | admin | 2026-10-12 00:59:52 +0000 (Mon, 12 Oct 2026) | 1 line

first commit on compose
------------------------------------------------------------------------
r1 | admin | 2026-10-12 00:59:51 +0000 (Mon, 12 Oct 2026) | 1 line

initial import
------------------------------------------------------------------------
  • 认证文件 /etc/svn/svn.htpasswd 与 /etc/svn/authz 每次启动按环境变量重新生成(文件不存在时);若要保留手工维护的口令文件,把第八节的 conf 挂载一并配上即可。

六、常用环境变量表

变量默认值作用
SVN_USERadminentrypoint 首次生成 htpasswd/authz 时使用的账号
SVN_PASSq1w2e3r4与 SVN_USER 配套的口令(仅首次生成时使用)
REPO_NAMErepo1首次启动时在 /srv/svn 下自动创建的版本库名

⚠️ 三个变量只在「对应文件/目录不存在」时生效:htpasswd 已存在时改 SVN_PASS 不会更新口令,需删除 /etc/svn/svn.htpasswd 或用 htpasswd 命令重置(见 11.4)。

七、常用配置参数说明(entrypoint 与 dav_svn)

配置项位置默认值/示例说明
SVNParentPathconf/dav_svn.conf/srv/svnhttp://host/svn/ 下可浏览的版本库父目录
AuthType / AuthNameconf/dav_svn.confBasic / Subversion RepositoryBasic 认证类型与提示域
AuthUserFileconf/dav_svn.conf/etc/svn/svn.htpasswdhtpasswd 口令文件路径
AuthzSVNAccessFileconf/dav_svn.conf/etc/svn/authzmod_authz_svn 路径级授权文件
Require valid-userconf/dav_svn.conf启用全部请求强制认证;改为 LimitExcept 包裹可实现匿名读
SVN_USER/SVN_PASS/REPO_NAMEentrypoint.shadmin / q1w2e3r4 / repo1首启自动建账号与建库

八、配置文件持久化(conf 挂载)

镜像内已 COPY 一份 dav_svn.conf;若希望不重建镜像即可调整配置,把宿主机配置目录挂载覆盖镜像内路径即可。在 docker-compose.yml 的 volumes 中追加:

    volumes:
      - ./svn-data:/srv/svn
      - ./conf/dav_svn.conf:/etc/apache2/mods-enabled/dav_svn.conf
      - ./conf/authz:/etc/svn/authz
      - ./conf/htpasswd:/etc/svn/svn.htpasswd

配套在宿主机准备 conf/authz(路径级授权)与 conf/htpasswd(htpasswd -cb conf/htpasswd admin q1w2e3r4 生成)。调整后 docker compose restart svn 生效。authz 文件实测内容:

[groups]
dev = admin

[/]
@dev = rw

⚠️ 挂载单文件要求宿主机文件必须先存在,否则 Docker 会自动创建同名目录导致 Apache 读取失败;先建文件再 up。

九、等价 docker run 运行方式

先构建一次镜像(或复用 compose 已构建的 svn-server:1.14),然后:

docker run -d --name svn-server-run \
    -p 18081:80 \
    -e SVN_USER=admin -e SVN_PASS=q1w2e3r4 -e REPO_NAME=repo1 \
    -v /opt/svn-compose/svn-data-run:/srv/svn \
    --restart unless-stopped \
    svn-server:1.14

💡 与 compose 方式完全等价:镜像内 entrypoint 负责建库与认证文件,-v 挂载负责数据持久化。compose 的价值在于把镜像版本、环境变量、挂载、重启策略收敛到一个 YAML 里管理。

十、常用运维命令

docker compose ps                     # 查看服务状态
docker compose logs --tail 20 svn     # 查看服务日志(建库/htpasswd 输出也在这里)
docker compose restart svn            # 重启服务(改挂载配置后用)
docker compose stop svn               # 停止(保留数据卷)
docker compose down                   # 停止并移除服务(bind mount 数据仍在宿主机)
docker compose up -d                  # 再次拉起

版本库完整性校验(在服务内执行,实测回显):

$ docker compose exec svn svnadmin verify /srv/svn/repo1
* Verifying metadata at revision 0 ...
* Verifying repository metadata ...
* Verified revision 0.
* Verified revision 1.
* Verified revision 2.

重启后状态确认:

$ docker compose restart svn
 Container svn-server Started
$ docker compose ps
NAME         IMAGE             COMMAND                   SERVICE   CREATED         STATUS         PORTS
svn-server   svn-server:1.14   "/usr/local/bin/entr…"   svn       8 minutes ago   Up 2 seconds   0.0.0.0:18080->80/tcp, [::]:18080->80/tcp
$ docker compose logs --tail 2 svn
svn-server  | Adding password for user admin

版本库冷备(直接打包宿主机数据目录):

tar -czf svn-data-$(date +%F).tar.gz svn-data/

十一、常见问题(FAQ)

11.1:启动报 failed to bind host port ... address already in use

实测现象:首次用 8080 端口发布时,docker compose up -d 报 Error response from daemon: failed to set up container networking: ... failed to bind host port 0.0.0.0:8080/tcp: address already in use。 定位:宿主机 8080 已被其他服务占用。解决:把 docker-compose.yml 的 ports 改为空闲端口(本例改 18080:80)后重新 docker compose up -d,或 ss -tlnp | grep 8080 找到占用进程处理。

11.2:访问返回 401 或 E215004: No more credentials or we tried too many times. Authentication failed

定位:401 是未带凭据的预期行为(Basic 认证生效);E215004 是用户名/口令错误。解决:curl 加 -u admin:q1w2e3r4,svn 命令加 --username admin --password q1w2e3r4 --non-interactive。

11.3:重建服务后版本库 404 / 空

定位:docker-compose.yml 漏配 - ./svn-data:/srv/svn,版本库建在容器可写层,docker compose down 后随容器删除。解决:补上挂载后 docker compose up -d,entrypoint 会在空数据目录里重新 svnadmin create;历史数据如已被删则需从备份 tar 包恢复到 svn-data/。

11.4:修改 SVN_PASS 不生效

定位:entrypoint 只在 /etc/svn/svn.htpasswd 不存在时才生成口令文件。解决:直接进服务重置口令:docker compose exec svn htpasswd -b /etc/svn/svn.htpasswd admin q1w2e3r4;或按第八节挂载了 conf/htpasswd 时,在宿主机重新生成该文件后 docker compose restart svn。

11.5:提交报 Permission denied

定位:bind mount 的宿主机目录属主不是 www-data(entrypoint 已内置 chown -R www-data:www-data /srv/svn 兜底,手动改过属主或挂载了只读目录时会触发)。解决:确认挂载未带 :ro,并在宿主机执行 chown -R 33:33 svn-data/(33 为 www-data 的 uid/gid)后重启服务。

十二、参考链接