LittleLink Docker Compose 部署指南

LittleLink v3.11.0 Docker Compose 部署指南:官方源码构建镜像(无预构建镜像)、site 目录挂载免重启改配置、多实例复用镜像、等价 docker run 与升级回滚,含空挂载 403、端口冲突、图标 404 三条排障实录,Ubuntu 24.04.5 全量实测回显。

LittleLinkLittleLinkDocker Compose自托管面板2026/11/03

LittleLink Docker Compose 部署指南

目标:在 Ubuntu 24.04.5 LTS 服务器上用 Docker Compose 从零部署 LittleLink v3.11.0 个人链接导航页,覆盖源码获取、镜像构建、站点目录挂载、多实例复用、日常运维与三条排障实录。全部命令与回显均已在 Ubuntu 24.04.5 LTS(x86_64)、Docker 29.9.2 / Docker Compose v5.6.1 环境实测通过,站点监听 8141 端口。

一、环境信息

本文部署环境为单台真实服务器,关键版本与规划如下:

项目值
操作系统Ubuntu 24.04.5 LTS(7.0.0-38-generic,x86_64)
Docker29.9.2(build a82e528)
Docker Composev5.6.1
LittleLinkv3.11.0(2026-07-28 发布,MIT 许可)
基础镜像nginx:alpine(nginx/1.31.8)
自建镜像littlelink:v3.11.0(95.1MB)
站点目录/opt/littlelink/site(bind mount 进容器)
对外端口主实例 8141、第二实例 8144、docker run 演示 8145
自定义网络10.89.68.0/24(主)、10.89.69.0/24(第二实例)

宿主机版本实测回显:

docker --version
docker compose version
head -2 /etc/os-release
uname -mr
Docker version 29.9.2, build a82e528
Docker Compose version v5.6.1
PRETTY_NAME="Ubuntu 24.04.5 LTS"
NAME="Ubuntu"
7.0.0-38-generic x86_64

💡 技术拓展:LittleLink 的部署形态不止 Docker Compose 一种,常见渠道全量对比——

形态原理适用场景
静态托管(Cloudflare Pages/GitHub Pages)直接把 index.html 等静态文件推到托管平台,官方仓库自带 wrangler.toml无服务器、要免费公网域名
任意 Web 服务器直放把站点目录拷到 Nginx/Apache 根目录已有 Web 服务器,顺手多挂一个站
Docker Compose(本文)官方 docker/Dockerfile(nginx:alpine + 站点文件)构建镜像,site 目录 bind mount服务器已容器化、要版本隔离与一键启停
纯静态镜像挂载不构建 LittleLink 镜像,直接用 nginx:alpine 挂 site不想自建镜像(见「九、等价 docker run」的变体讨论)

本文采用 Docker Compose 形态,理由:与本机其他自托管组件统一管理,且官方仓库自带 Docker 支持(v3.6.0 起,PR #151),compose 文件即官方形态。

二、镜像选型说明

本节确定「用什么镜像跑 LittleLink」。关键事实:LittleLink 官方没有在 Docker Hub/ghcr 发布预构建镜像,官方仓库 docker/ 目录提供的是 Dockerfile + compose.yaml,官方推荐的容器化方式就是从源码构建——把整个项目(含站点静态文件)COPY 进 nginx:alpine,再把站点目录挂载出去供修改。

三种可选路线:

路线做法优点缺点
A. 官方源码构建(本文)build: ./littlelink-3.11.0 + docker/Dockerfile与官方一致、版本可锁定、镜像自带兜底站点需保留源码目录做构建上下文
B. 复用已构建镜像多实例共用 littlelink:v3.11.0,各自挂不同 site只构建一次,秒级起新实例首次仍需路线 A 构建
C. 裸 nginx:alpine 挂载image: nginx:alpine + 挂 site + 自备 nginx 配置零构建丢失官方内联 nginx 配置(gzip/缓存/日志到 stdout),需自己维护

镜像体积实测(构建完成后):

docker images littlelink:v3.11.0
docker images nginx:alpine
IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
littlelink:v3.11.0   67a220909046       95.1MB         26.8MB   U    
nginx:alpine         785835ea65db       94.5MB         27.3MB        

LittleLink 镜像比裸 nginx:alpine 仅重约 0.6MB(站点静态文件压缩后增量),因为镜像内容就是「nginx:alpine + 一份站点文件 + 一段内联 nginx 配置」。解读:选路线 A 构建一次、多实例走路线 B;路线 C 仅在无法执行构建的环境(如只允许预置镜像的运行时)考虑,且需自行补齐「七、配置参数说明」中的 nginx 配置。

三、前置条件

3.1 Docker 与 Compose 就绪

Docker 29.9.2 与 Compose v5.6.1 已安装(回显见「一、环境信息」),无需额外组件——LittleLink 无数据库、无运行时依赖,nginx:alpine 基础镜像本机已存在:

docker images nginx:alpine
IMAGE          ID             DISK USAGE   CONTENT SIZE   EXTRA
nginx:alpine   785835ea65db       94.5MB         27.3MB        

nginx 具体版本号(1.31.8)在部署完成后由响应头 Server: nginx/1.31.8 验证(见 4.3),无需单独探测。

3.2 端口空闲检查

主实例计划使用 8141 端口,先确认未被占用:

ss -ltn | grep ':8141' || echo "8141 空闲"
8141 空闲

⚠️ 注意:8142 端口在本机被 IDE 组件占用,规划端口时请避开;端口冲突的实际报错与处理见「十二、常见问题」Q2。

3.3 获取官方源码(含网络降级实录)

官方仓库为 sethcottle/littlelink(官网 littlelink.io 指向的同一仓库)。首选 git clone 拉取 v3.11.0 标签:

mkdir -p /opt/littlelink && cd /opt/littlelink
git clone --branch v3.11.0 --depth 1 https://github.com/sethcottle/littlelink.git littlelink-3.11.0

当时实测该命令在本机无输出挂起直至超时(复测 git ls-remote https://github.com/sethcottle/littlelink.git HEAD 限时 30 秒同样无任何输出、退出码 124),代理通道亦不可达;改用 curl 直连 GitHub 官方 tarball 成功——这是当前实测唯一可用渠道:

cd /opt/littlelink
curl -LO https://github.com/sethcottle/littlelink/archive/refs/tags/v3.11.0.tar.gz

输出节选(实测末行进度):

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  432k    0  432k    0     0   205k      0 --:--:--  0:00:02 --:--:--   423k
ls -l v3.11.0.tar.gz
tar -xzf v3.11.0.tar.gz
ls littlelink-3.11.0
-rw-r--r-- 1 root root 442372 Nov  3 02:11 v3.11.0.tar.gz
css
docker
fonts
images
index.html
LICENSE.md
privacy.html
README.md
VERSION.md
wrangler.toml

⚠️ 网络情况(2026-11-02/03 实测):git clone/git ls-remote 直连 GitHub 超时不可用、代理 192.168.1.62:7897 不可达;curl 直连 https://github.com/sethcottle/littlelink/archive/refs/tags/v3.11.0.tar.gz 稳定可用(442372 字节)。复现本文时若 git 已恢复则优先 git,否则按上述 tarball 渠道下载,并校验版本(head -4 VERSION.md 首行应为 # LittleLink Version History、## Current Version: v3.11.0)。

3.4 准备站点目录

部署采用「镜像内置站点 + bind mount 覆盖」模式,需要先把站点文件从源码目录复制成独立的可写目录(后续所有编辑都发生在 site/,与源码目录解耦):

cd /opt/littlelink
mkdir -p site
cp -r littlelink-3.11.0/{css,fonts,images,index.html,privacy.html} site/
du -sh site
984K	site

站点目录共 984K:index.html(460 行、121 个示例按钮)、css/ 3 个样式表、images/icons/ 123 个品牌 SVG 图标、fonts/ 4 个 woff2 字体、privacy.html 隐私页。解读:这就是 LittleLink 的全部「数据」——纯静态文件,没有编译产物、没有数据库。

四、部署步骤

4.1 编写 compose.yaml

在 /opt/littlelink/ 下创建 compose.yaml:

name: littlelink

services:
  web:
    build:
      context: ./littlelink-3.11.0
      dockerfile: docker/Dockerfile
    image: littlelink:v3.11.0
    ports:
      - "8141:80"
    volumes:
      - ./site:/usr/share/nginx/html
    networks:
      - littlelink-net
    restart: unless-stopped

networks:
  littlelink-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.68.0/24

逐项说明:

  • name: littlelink:compose 项目名,决定容器/网络前缀(容器 littlelink-web-1、网络 littlelink_littlelink-net);
  • build.context / build.dockerfile:以源码目录为构建上下文、使用官方 docker/Dockerfile(内容见「七、配置参数说明」);
  • image: littlelink:v3.11.0:给构建产物显式命名打标签,供多实例复用(见「十、多实例部署」);
  • ports: "8141:80":容器内 nginx 监听 80,映射到宿主 8141;
  • volumes: ./site:/usr/share/nginx/html:站点目录覆盖镜像内置副本,改文件即生效、无需重建镜像;
  • restart: unless-stopped:宿主机重启后自动拉起,手动 stop 则不复活;
  • networks + 固定 subnet:显式网段便于与本机其他组件网段隔离排查(不写也能跑,Docker 自动分配)。

💡 技术拓展:restart 策略共四档——

策略行为适用
no(默认)退出即终止一次性任务
on-failure[:N]非 0 退出码才重启,可限次数批处理
always无条件重启,宿主重启后也拉起必须常驻的服务
unless-stopped同 always,但尊重手动 stop本文这类常驻但需人工停用的服务

本文用 unless-stopped:既保证宿主机重启自动恢复,又允许排障时 docker compose stop 后不被自动拉起干扰。

4.2 构建并启动

cd /opt/littlelink
docker compose up -d --build

输出节选(实测首尾):

#10 naming to docker.io/library/littlelink:v3.11.0 done
#10 unpacking to docker.io/library/littlelink:v3.11.0 0.0s done
#10 DONE 0.1s

#11 resolving provenance for metadata file
#11 DONE 0.0s
 Image littlelink:v3.11.0 Built 
 Network littlelink_littlelink-net Created 
 Container littlelink-web-1 Created 
 Container littlelink-web-1 Starting 
 Container littlelink-web-1 Started 

4.3 验证运行状态

docker compose ps
NAME               IMAGE                COMMAND                  SERVICE   CREATED          STATUS          PORTS
littlelink-web-1   littlelink:v3.11.0   "/docker-entrypoint.…"   web       12 seconds ago   Up 11 seconds   0.0.0.0:8141->80/tcp, [::8141->80/tcp]
curl -sI http://localhost:8141/
HTTP/1.1 200 OK
Server: nginx/1.31.8
Date: Mon, 02 Nov 2026 17:52:09 GMT
Content-Type: text/html
Content-Length: 31818
Last-Modified: Mon, 02 Nov 2026 17:52:09 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "6ae8cdc9-7c4a"
Accept-Ranges: bytes

解读:200 OK + Server: nginx/1.31.8 说明镜像内 nginx 正常服务默认 index.html(31818 字节即官方默认页大小);Content-Length 可在改动站点后用于快速判断生效与否。

curl -s http://localhost:8141/ | grep -nE 'class="theme|<title>' | head -3
10:<html class="theme-auto" lang="en"> <!-- Update `class="theme-auto"` with your preference -->
17:    <title>LittleLink</title>

首屏确认为官方默认状态:theme-auto(跟随系统深浅色)+ 默认标题。主题与按钮的定制方法见本专栏配置详解篇(发布后互链)。

docker compose logs --tail 5
web-1  | 2026/11/02 17:52:47 [notice] 1#1: start worker process 30
web-1  | 2026/11/02 17:52:47 [notice] 1#1: start worker process 31
web-1  | 2026/11/02 17:52:47 [notice] 1#1: start worker process 32
web-1  | 2026/11/02 17:52:47 [notice] 1#1: start worker process 33
web-1  | 2026/11/02 17:52:47 [notice] 1#1: start worker process 34

解读:nginx worker 进程正常拉起;官方 Dockerfile 把 access/error 日志软链到 stdout/stderr,因此 docker compose logs 可直接看到访问记录(见 4.4)。

4.4 验证网络与访问日志

docker network inspect littlelink_littlelink-net --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
10.89.68.0/24
curl -s -o /dev/null http://localhost:8141/ && docker compose logs --tail 2
web-1  | 10.89.68.1 - - [02/Nov/2026:17:52:09 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.5.0"
web-1  | 10.89.68.1 - - [02/Nov/2026:17:52:09 +0000] "GET / HTTP/1.1" 200 31818 "-" "curl/8.5.0"

解读:10.89.68.1 是宿主在自定义网络中的网关地址,说明请求确实经 8141→80 转发进入站点;访问日志实时进入容器 stdout。

五、数据持久化说明

LittleLink 是纯静态站点,没有数据库、没有应用级状态,「数据」就是 /opt/littlelink/site/ 目录本身:

数据位置持久化方式
页面与配置(index.html 即配置)/opt/littlelink/site/bind mount,宿主目录即事实源
品牌图标/字体/样式/opt/littlelink/site/images、fonts、css同上
镜像Docker 存储可随时由源码重建(up -d --build),无需备份
日志容器 stdout由 Docker 日志驱动管理(默认 json-file)

备份即打包站点目录:

tar -czf site-$(date +%F).tar.gz -C . site
ls -lh site-*.tar.gz
-rw-r--r-- 1 root root 415K Nov  3 02:15 site-2026-11-03.tar.gz

解读:415K 的 tar 包即全部可恢复状态;docker compose down 不会删除 site/(bind mount 宿主目录不受影响),重建实例只需恢复该目录。

六、环境变量表

LittleLink v3.11.0 官方 Dockerfile 不消费任何环境变量——它不是应用运行时,而是「静态文件 + nginx」。不存在 LISTEN_PORT、BASE_URL 之类参数;页面里的一切可配置项都在 site/index.html 内(数据模型详解见本专栏配置篇)。因此「环境变量表」在本文的对应物是 compose 服务级配置项表:

compose 键本文取值默认值说明
build.context./littlelink-3.11.0—镜像构建上下文(官方源码目录)
build.dockerfiledocker/DockerfileDockerfile官方 Dockerfile 路径
imagelittlelink:v3.11.0<项目>-web显式命名供多实例复用
ports"8141:80"—容器内固定 80(Dockerfile 写死 listen 80),改端口只动宿主侧
volumes./site:/usr/share/nginx/html—站点目录挂载,核心配置通道
restartunless-stoppedno见 4.1 技术拓展
networks自定义 bridge + 固定 subnetdefault隔离与可排查性

⚠️ 注意:想改容器内监听端口不能靠环境变量——listen 80 内联在镜像的 /etc/nginx/conf.d/default.conf 里,正确做法是改 compose 的宿主侧端口映射,或自建 Dockerfile 覆盖该配置(见「七、7.1」)。

七、配置参数说明(镜像内 nginx 配置)

7.1 官方 Dockerfile 逐段解读

镜像由官方 docker/Dockerfile 构建,全文如下(v3.11.0 源码 cat docker/Dockerfile 实测内容):

FROM nginx:alpine

# Copy static website files
COPY . /usr/share/nginx/html/

# Configure nginx with basic optimization and logging to stdout/stderr
RUN echo 'server { \
    listen 80; \
    server_name localhost; \
    root /usr/share/nginx/html; \
    index index.html index.htm; \
    \
    # Enable access logging to stdout \
    access_log /dev/stdout; \
    error_log /dev/stderr; \
    \
    # Enable gzip compression \
    gzip on; \
    gzip_vary on; \
    gzip_types text/plain text/css application/json application/javascript; \
    \
    # Basic cache settings \
    location ~* \\.(?:css|js|jpg|jpeg|gif|png|ico|svg)$ { \
        expires 7d; \
        add_header Cache-Control "public"; \
    } \
}' > /etc/nginx/conf.d/default.conf

# Forward nginx logs to Docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

关键配置逐项说明:

nginx 指令取值含义与影响
listen / server_name80 / localhost容器内固定 80 端口,server 名仅兜底
root / index/usr/share/nginx/html / index.html index.htm站点根即挂载点;默认页优先 index.html
access_log / error_log/dev/stdout / /dev/stderr日志进容器标准流,docker compose logs 可见
gzip on + gzip_typestext/css、js、json 等文本资源压缩传输
静态资源 locationexpires 7d + Cache-Control: publiccss/js/图片/SVG 缓存 7 天

7.2 配置生效验证(实测)

gzip 与缓存头实测:

curl -sI -H 'Accept-Encoding: gzip' http://localhost:8141/css/brands.css | grep -iE 'HTTP|content-encoding|vary'
curl -sI http://localhost:8141/css/brands.css | grep -iE 'HTTP|expires|cache-control'
curl -sI http://localhost:8141/ | grep -iE 'HTTP|cache-control|expires'
HTTP/1.1 200 OK
Vary: Accept-Encoding
Content-Encoding: gzip
HTTP/1.1 200 OK
Expires: Mon, 09 Nov 2026 18:02:36 GMT
Cache-Control: max-age=604800
Cache-Control: public
HTTP/1.1 200 OK

解读:brands.css(11597 字节)走 gzip 压缩、带 7 天缓存头(max-age=604800 = 7×86400);而 index.html 不匹配静态资源 location、无缓存头——这正是期望行为:入口页永远回源验证,静态资源长期缓存。改完 index.html 浏览器立即能拿到新内容,改 css/图标则最多滞后 7 天(强制刷新即可,见 Q4)。

八、配置文件挂载

本方案唯一需要「挂载」的配置文件就是站点目录:./site:/usr/share/nginx/html。工作机制:

  1. 构建时 COPY . 已把源码目录整体打入镜像 /usr/share/nginx/html(兜底副本);
  2. 运行时 bind mount 用宿主 site/ 覆盖该目录——镜像内副本被遮蔽,nginx 读到的全部是宿主文件;
  3. 因此修改站点文件不需要重启容器,nginx 对静态文件按请求读盘。

实测验证「免重启生效」——修改标题与主题后直接抓取:

cd /opt/littlelink/site
sed -i 's|<html class="theme-auto"|<html class="theme-dark"|' index.html
sed -i 's|<title>LittleLink</title>|<title>Quzuo Labs - 链接导航</title>|' index.html
curl -s http://localhost:8141/ | grep -nE 'class="theme|<title>' | head -3
10:<html class="theme-dark" lang="en"> <!-- Update `class="theme-auto"` with your preference -->
17:    <title>Quzuo Labs - 链接导航</title>

全程无 restart、无 --build,改动即时可见。解读:这就是把「配置」放在挂载目录而非镜像内的价值——配置变更与镜像解耦。若挂载路径写错(如目录不存在),compose 会自动创建空目录挂进去,nginx 因找不到 index.html 且未开目录列表而返回 403——完整排障实录见 Q1。

九、等价 docker run

compose 主服务等价的一次性 docker run(用于无 compose 环境或临时验证):

cd /opt/littlelink
docker run -d --name littlelink-standalone -p 8145:80 -v "$PWD/site:/usr/share/nginx/html" littlelink:v3.11.0
c7b472a6a4addd6b2f2cbae42db7ea64082be5ba506b3fd27983691434f2ae55
curl -sI http://localhost:8145/ | head -6
HTTP/1.1 200 OK
Server: nginx/1.31.8
Date: Mon, 02 Nov 2026 17:52:47 GMT
Content-Type: text/html
Content-Length: 32405
Last-Modified: Mon, 02 Nov 2026 17:52:10 GMT

解读:同一镜像、同一挂载,docker run 与 compose 行为一致(Content-Length: 32405 是「八、配置文件挂载」改过标题的定制页大小,与 compose 实例相互印证)。差异仅在:--restart unless-stopped 需显式加 --restart,自定义网络需 --network,且无 compose ps/logs 的编排便利。验证完清理演示实例:

docker rm -f littlelink-standalone

十、多实例部署(复用镜像)

LittleLink 单实例即一个导航页;为不同用户/场景开多个页面时,复用 littlelink:v3.11.0 镜像、每个实例独立项目目录与站点目录即可,不必重复构建。以第二实例(Jane 的个人页)为例,目录 /opt/littlelink-jane/:

name: littlelink-jane

services:
  web:
    image: littlelink:v3.11.0
    ports:
      - "8144:80"
    volumes:
      - ./site:/usr/share/nginx/html
    networks:
      - littlelink-net
    restart: unless-stopped

networks:
  littlelink-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.69.0/24

与主实例 compose 的差异只有三处:name(项目隔离)、ports(8144)、subnet(10.89.69.0/24,避开主实例网段);image 直接引用已构建镜像、无 build 段。

启动与验证(实测):

cd /opt/littlelink-jane
mkdir -p site && cp -r /opt/littlelink/site/. site/
docker compose up -d
docker compose ps
 Container littlelink-jane-web-1 Created 
 Container littlelink-jane-web-1 Starting 
 Container littlelink-jane-web-1 Started 
NAME                    IMAGE                COMMAND                  SERVICE   CREATED                  STATUS                  PORTS
littlelink-jane-web-1   littlelink:v3.11.0   "/docker-entrypoint.…"   web       Less than a second ago   Up Less than a second   0.0.0.0:8144->80/tcp, [::8144->80/tcp]
curl -s http://localhost:8144/ | grep -nE 'class="theme|<title>' | head -3
docker network inspect littlelink-jane_littlelink-net --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
10:<html class="theme-light" lang="en"> <!-- Update `class="theme-auto"` with your preference -->
17:    <title>Jane Doe - 个人链接页</title>
10.89.69.0/24

解读:第二实例的 site/ 独立编辑(示例中已改为 theme-light + 个人标题),与主实例互不影响;两实例各自独立网络与端口,docker compose ps 互不串扰。新增 N 个页面即复制该模式 N 份(端口、网段逐个错开)。

十一、常用运维命令

以下命令均在 /opt/littlelink/ 项目目录内执行,回显为实测。

11.1 状态与日志

docker compose ps
docker compose logs --tail 5

回显见 4.3/4.4 节。日志即 nginx access/error 流,排查 404/403 首选此处(见 Q1、Q3)。

11.2 停止、启动与重建

docker compose stop
docker compose ps -a
 Container littlelink-web-1 Stopping 
 Container littlelink-web-1 Stopped 
NAME               IMAGE                COMMAND                  SERVICE   CREATED          STATUS                              PORTS
littlelink-web-1   littlelink:v3.11.0   "/docker-entrypoint.…"   web       10 minutes ago   Exited (0) Less than a second ago   
docker compose up -d
 Container littlelink-web-1 Starting 
 Container littlelink-web-1 Started 

解读:stop 保留容器(Exited (0),配置不变),up -d 原地拉起;unless-stopped 策略下手动 stop 不会被自动重启干扰。

进入站点目录核对文件(不依赖宿主副本视角):

docker compose exec web sh -c 'ls /usr/share/nginx/html | head'
css
fonts
images
index.html
privacy.html

11.3 升级与回滚

升级 = 确认新版本号 → 下载新源码 → 重建镜像 → 按需同步站点新增文件。版本号不硬编码,用官方发布订阅源实查(实测回显,最新 release 更新时间 2026-07-29 即 v3.11.0):

curl -s https://github.com/sethcottle/littlelink/releases.atom | grep -m3 -E '<title>|<updated>'
  <title>Release notes from littlelink</title>
  <updated>2026-07-29T04:31:15Z</updated>
    <updated>2026-07-29T04:49:45Z</updated>

拿到新版本号 vX.Y.Z 后,按 3.3 节同款命令下载对应 tarball(https://github.com/sethcottle/littlelink/archive/refs/tags/vX.Y.Z.tar.gz,URL 中的 vX.Y.Z 替换为上一步实查到的版本号)并解压为新源码目录,然后重建镜像并原地滚动更新:

docker compose up -d --build

回显与 4.2 节一致(Image littlelink:v3.11.0 Built → Container littlelink-web-1 Started,镜像名/版本号随 image: 键同步调整)。

⚠️ 注意:site/ 目录不会被升级自动覆盖——若新版新增了 css/图标文件(如 v3.11.0 新增的 .button-yt-music 类与重制的 Google 系列图标),需手工把新版源码中的 css/brands.css、images/icons/ 增量同步进 site/,否则新按钮类无样式、新图标 404(现象与定位见 Q3)。回滚则把 build.context 指回旧版源码目录重新 up -d --build,站点目录用「五、」的 tar 备份恢复。

11.4 彻底卸载

docker compose down
 Container littlelink-web-1 Stopping 
 Container littlelink-web-1 Stopped 
 Container littlelink-web-1 Removing 
 Container littlelink-web-1 Removed 
 Network littlelink_littlelink-net Removing 
 Network littlelink_littlelink-net Removed 
docker rmi littlelink:v3.11.0
rm -rf /opt/littlelink

解读:down 只删容器与网络,不动 site/ 数据;确认不再需要后再删镜像与目录。

十二、常见问题

Q1:站点目录挂成了空目录,页面返回 403 Forbidden。

现象(实测复现:故意把一个空目录挂进站点根):

mkdir -p /opt/littlelink/empty-site
docker run -d --name littlelink-empty -v /opt/littlelink/empty-site:/usr/share/nginx/html littlelink:v3.11.0
docker exec littlelink-empty sh -c 'ls -A /usr/share/nginx/html | wc -l'
IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' littlelink-empty)
curl -sI http://$IP:80/ | head -2
0
HTTP/1.1 403 Forbidden
Server: nginx/1.31.8

定位:挂载点内文件数为 0——nginx 找不到 index.html,而目录列表(autoindex)默认关闭,对目录本身返回 403 而非 404。根因通常是 compose 里 volumes 路径写错,Docker 自动创建了不存在的宿主目录。解决:清理演示实例,核对 volumes 指向真实站点目录(./site)并把文件复制进去,再复验:

docker rm -f littlelink-empty
curl -sI http://localhost:8141/ | head -1
littlelink-empty
HTTP/1.1 200 OK

Q2:端口被占用,容器起不来。

现象(实测:对已占用端口再起一个实例):

docker run -d --name littlelink-dup -p 8141:80 littlelink:v3.11.0
docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint littlelink-dup (9a5f1c9ee8e93c5ff7cea666a51213a9380ff4f87b8dfaefb0df14894dc7380a): Bind for 0.0.0.0:8141 failed: port is already allocated

Run 'docker run --help' for more information

定位与解决:报错明确指出 8141 已分配。用 ss -ltnp | grep 8141 找到占用者;若是遗留实验实例,docker rm -f 后重试;若端口规划冲突,改 compose ports 宿主侧端口(如 8144,见「十、」)并同步防火墙/反代配置。

Q3:按钮图标不显示,日志里一片 404。

现象(实测:引用了不存在的图标文件):

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8141/images/icons/nonexistent.svg
docker compose logs --tail 2
404
web-1  | 2026/11/02 17:52:10 [error] 29#29: *10 open() "/usr/share/nginx/html/images/icons/nonexistent.svg" failed (2: No such file or directory), client: 10.89.68.1, server: localhost, request: "GET /images/icons/nonexistent.svg HTTP/1.1", host: "localhost:8141"
web-1  | 10.89.68.1 - - [02/Nov/2026:17:52:10 +0000] "GET /images/icons/nonexistent.svg HTTP/1.1" 404 153 "-" "curl/8.5.0"

定位:error 日志精确给出缺失文件在站点根下的路径。根因两类:手写按钮时 src 拼错,或升级后新增按钮引用了尚未同步的图标。解决:对照 site/images/icons/ 下 123 个官方 SVG 文件名修正 src,或从新版源码补齐图标文件;无需重启。

Q4:改了 CSS/图标,浏览器还是旧样式。

定位:镜像内 nginx 对 css/js/图片/SVG 设置了 expires 7d(实测响应头 Cache-Control: max-age=604800,见 7.2),而 index.html 无缓存头。所以「页面结构变了但样式没变」通常是静态资源缓存所致。解决:强制刷新(Ctrl+Shift+R);长期方案是在反代层对静态资源加版本查询串或缩短缓存期(需自建 Dockerfile 覆盖 default.conf,见「六、」注意事项)。

十三、参考链接