
一块树莓派能干什么放在客厅吃灰是常态但对一个玩自托管的人来说它就是一台能 24 小时开机的私人服务器。我手里这块树莓派 4B 已经用 Docker 稳定跑了快两年上面挂着网页服务器、数据库、几个定时任务整机功耗还不如一个路由器。这篇专栏导览就把“树莓派 Docker 网页服务器”这条链路从头到尾讲清楚包括为什么这么选、怎么一步步跑起来、哪些坑我替你踩过了以及后面还能往哪些方向继续折腾。无论你是刚把树莓派从抽屉里翻出来还是已经刷好系统准备找个正经用途这篇文章都适合你。1. 树莓派跑网页服务器为什么我坚持用 Docker1.1 树莓派适合做哪类网页服务器先说结论树莓派不是生产型服务器但它非常适合个人网站、博客、内网工具、家庭服务这类“低并发、长在线”的场景。我自己在上面跑过一个 WordPress 博客、一个静态文档站还有一个内部用的数据查询页PV 不高但胜在全年无休。树莓派 4B 的功耗通常在 3 到 7 瓦之间比大部分家用 NAS 还低而且体积小、噪音为零。只要配一张质量说得过去的 TF 卡或者 SSD它就能安安静静待在弱电箱或者书架上。对比动辄几十瓦的旧台式机它是我能接受“24 小时不关机”的最舒服形态。但这也决定了不能用常规思路去部署。你不可能指望像云服务器那样一键买完就 SSH 上去装环境所有东西都得自己搭。而且 ARM 架构的行为习惯和 x86 的服务器不太一样很多软件包看着能用装完才发现版本或者依赖有问题。这个痛点恰恰是 Docker 最擅长解决的。1.2 容器化相比直接装 Nginx 的三个实打实优势有人会问树莓派上直接sudo apt install nginx不行吗能跑但会有几个麻烦。第一环境隔离。直接装在系统里Nginx 的配置文件散落在/etc/nginx网站文件扔在/var/www数据库再一装整个系统很快变成一个“改哪里都怕碰坏别处”的状态。用 Docker 隔离开每个容器就是一个独立的小房间删了重建都不会影响系统本体。第二可复现。你在 A 树莓派上折腾好的环境想迁移到 B 机器或者几个月后自己都忘了当时改过什么这时候只需要一份 Docker Compose 文件加一个数据目录就能原样复制整个服务。这个能力在个人项目里尤其重要因为个人项目往往没有严谨的运维文档唯一能依赖的就是配置本身。第三版本可控。直接装系统包Nginx、PHP、MySQL 的版本都被操作系统锁死。想升级等系统源更新。想固定在某个版本难。Docker 里你想用 1.20 还是 1.26 的 Nginx改个镜像标签就行彻底摆脱“系统源里只有旧版”的尴尬。1.3 硬件与系统选型4B 仍然是性价比之王树莓派 4B 是现在玩服务器最合适的选择原因很简单内存选项多2GB、4GB、8GB 都有。如果你只是跑 Nginx 和静态页面2GB 都嫌多但如果你想顺便挂 MySQL、Redis甚至一个轻量后端服务推荐直接上 4GB 版本。我自己用 4GB日常跑 5 个容器内存占用在 60% 左右。树莓派 5 我也玩过性能更强还带 PCIe 接口可以接 M.2 SSD确实是好东西。但对“网页服务器”这个需求来说4B 完全足够省下的钱买一块好点的电源更实际。系统方面我推荐两种方案一是 Raspberry Pi OS Lite64 位二是 Ubuntu Server 22.04 / 24.04 的 ARM64 版本。Raspberry Pi OS 对树莓派硬件支持最好温度控制、摄像头、GPIO 这些生态最完整Ubuntu 的好处是更接近主流服务器环境网上资料最多而且 Docker 官方对 Ubuntu 的支持也特别积极。我树莓派 4B 上长期用的是 Ubuntu 22.04整体非常稳。2. 环境准备把树莓派从裸机变成 Docker 主机2.1 系统烧录推荐 Raspberry Pi OS Lite 还是 Ubuntu 22.04烧录工具我直接用 Raspberry Pi Imager官网下载选好系统镜像后写进 TF 卡。这里有个小技巧Imager 在烧录前有设置选项可以预先开启 SSH、设置用户名密码、配置 Wi-Fi。建议一开始就设置好省得显示器键盘来回插。如果你选 Ubuntu Server 22.04同样可以通过 Imager 直接选 Ubuntu 镜像烧完以后系统默认开启 SSH默认账号是ubuntu初始密码会在第一次登录时要求修改。用ssh ubuntu树莓派IP登录进去就能开始所有操作。我个人比较推荐 Ubuntu 22.04 还有一个原因它的内核和 Docker 的兼容性非常稳不会遇到很多边缘发行版常见的 cgroup、iptables 兼容问题。树莓派专用系统虽然直观但在“装 Docker 跑服务”这条路上Ubuntu 的通用性会帮你减少很多不必要的折腾。2.2 基础网络与镜像源配置系统装好后的第一件事不是急着装 Docker而是把基础网络和软件源调好。先给树莓派固定一个内网 IP。直接在路由器后台给 MAC 地址绑定 IP 最省事但如果没有路由器权限也可以在系统里改netplanUbuntu或者/etc/dhcpcd.confRaspberry Pi OS。固定 IP 很重要否则 DHCP 租约到期后树莓派换了地址你辛辛苦苦跑起来的网页服务就找不到了。然后是软件源。Ubuntu 22.04 默认源在国外更新速度比较慢建议把/etc/apt/sources.list换成国内可访问的镜像源。湾区的云服务商、高校镜像都有选一个响应快的就行。具体方法不展开核心就是把archive.ubuntu.com替换成你选中的镜像域名然后sudo apt update验证一下。这里我还想多说一句换源这件事不要盲目。如果你所在网络访问默认源很快就没必要动它。任何配置优化都应该以“实际效果”为准而不是“大家都换了所以我也要换”。2.3 安装 Docker Engine 并让普通用户免 sudo安装 Docker 有两种方式。一种是直接sudo apt install docker.io简单但版本可能旧一些另一种是用 Docker 官方安装脚本curl -fsSL https://get.docker.com | sh这个脚本会帮你装好 Docker Engine、containerd 和 compose 插件装完以后 Docker 服务会自动启动。我两次装系统都用官方脚本目前没有出过问题。装完以后先验证一下sudo docker run hello-world能看到 “Hello from Docker!” 就说明 Docker 已经跑起来了。但每次都加sudo很烦。执行下面这条命令把当前用户加进docker组sudo usermod -aG docker $USER然后重新登录 SSH或者newgrp docker之后就能直接docker ps不用再敲 sudo 了。这个操作的本质是调整权限组让当前用户有权访问 Docker 的 socket。安全性上所有能操作 Docker 的用户基本等同于拥有 root 权限所以只在自己用的树莓派上这样配不要在有其他用户的机器上随意开放。2.4 配置 Docker 镜像加速Docker 装好以后很多人第一个遇到的坑就是拉镜像特别慢。特别是nginx、mysql这种常用镜像动辄几百兆默认源很容易卡住。解决办法是配置 registry mirror。编辑/etc/docker/daemon.json{ registry-mirrors: [https://docker.m.daocloud.io] }改完重启 Dockersudo systemctl restart docker注意镜像加速只影响 Docker Hub 官方镜像仓库的拉取连接它本身是一个缓存转发服务不改变你拉取的镜像内容。配置完成后再拉镜像速度通常会有明显提升。如果还是慢可以考虑更换其他可访问的加速地址但不要同时堆太多域名一个能用的就够。2.5 顺带解释为什么不需要 Docker Desktop很多新手看到“Docker Desktop”就以为树莓派也要装这个。实际上Docker Desktop 是给 Windows 和 macOS 用的集成环境它依赖虚拟化层做 Linux 容器。你经常搜到的“virtualization support not detected”这类报错基本都发生在 Windows 上和树莓派没关系。树莓派跑的是 Linux 系统直接装 Docker Engine 就是最原生的方式。不存在 Hyper-V、虚拟化检测、npipe://这些概念所以不需要去碰 Docker Desktop。这一点想通了能少走很多弯路。3. 网页服务器容器化从 Nginx 到完整编排3.1 第一个容器让 Nginx 先跑起来现在开始正经事。先用一个最简单的命令把 Nginx 拉起来docker run -d --name web -p 80:80 nginx:stable-alpine-d表示后台运行--name web给容器起个名字-p 80:80把宿主机的 80 端口映射到容器内的 80 端口。镜像我建议用nginx:stable-alpine因为 alpine 版本体积小稳定版级别也比较克制适合跑长线服务。跑起来后浏览器里访问http://树莓派IP如果能看到 Nginx 的默认欢迎页说明第一个网页服务器容器已经工作正常。接下来用docker ps看看容器状态再用docker logs web看看访问日志。你能看到类似这样的记录192.168.x.x - - [xx/xxx/xxxx:xx:xx:xx 0000] GET / HTTP/1.1 200这个时候你会发现容器里写的日志会被 Docker 收集起来后续排查问题也全部围绕这些日志展开和传统服务器看/var/log/nginx/access.log的感觉完全不一样。3.2 写 Docker Compose 编排静态网站单跑一个 Nginx 容器只是开胃菜。真正有价值的是把整个服务用 Docker Compose 管理起来这样以后一条命令就能启动和停止全部服务。先建一个项目目录mkdir -p ~/services/web cd ~/services/web mkdir -p html在html目录里放一个index.html内容随意。然后在项目目录创建docker-compose.ymlservices: web: image: nginx:stable-alpine container_name: web ports: - 80:80 volumes: - ./html:/usr/share/nginx/html:ro restart: unless-stopped这个配置文件干了三件事映射端口、挂载网站目录、设置重启策略。第 19 行左右的./html:/usr/share/nginx/html是 bind mount把宿主机当前目录下的html文件夹挂进容器的 Nginx 网站根目录。后面的:ro表示只读挂载防止容器内进程意外修改到宿主机文件。挂载有一个容易踩坑的点如果挂载的是宿主机空目录容器内原来的默认文件会被这个空目录“遮住”所以你得自己放一个index.html进去否则会看到 403。启动docker compose up -d以后更新网站内容直接改宿主机的html目录刷新页面就能看到变化不需要重新构建容器。这是 Docker 化网页服务器最舒服的一点内容和运行环境彻底分开了。3.3 加入数据库和后端MySQL 与 Redis 容器化扩展静态页面只是起步。只要网站有一点点动态功能就需要数据库。最常见的搭配是 Nginx PHP-FPM MySQL或者 Nginx Node/Java/Go MySQL。Docker 的好处是每个组件都是独立容器并且通过自定义网络互相访问。我以前部署过一个带后台的博客在同一个 Compose 文件里加了 MySQL 和 Redis 服务services: web: image: nginx:stable-alpine container_name: web ports: - 80:80 volumes: - ./html:/usr/share/nginx/html:ro restart: unless-stopped depends_on: - db - redis db: image: mysql:8.0 container_name: db ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: 你的数据库密码 MYSQL_DATABASE: myweb volumes: - db_data:/var/lib/mysql restart: unless-stopped redis: image: redis:7-alpine container_name: redis ports: - 6379:6379 restart: unless-stopped volumes: db_data:这里的关键点是depends_on。它让 Docker Compose 在启动web容器前先启动db和redis容器避免应用一启动就连接不到数据库。另一个重点是db_data这块命名卷。它是 Docker 管理的持久化数据卷用来存储 MySQL 的数据文件。即使你执行docker compose down把容器全部删掉数据也不会丢下次重新up会继续使用旧数据。这是整个容器化方案里最容易忽略却在生产环境最致命的问题忘记做数据持久化一升级容器数据库全没了。我自己刚接触 Docker 时犯过这个错误当时直接删了一个 MySQL 容器然后整个人愣在原地。现在不管什么容器只要里面存了数据第一件事就是确认挂了持久化卷。3.4 数据持久化与备份迁移数据持久化是树莓派上跑 Docker 的重中之重因为树莓派的常见事故之一是 TF 卡损坏。卡一坏系统和容器都能重装但数据库里的数据可能就再也找不回来了。所以在跑通服务之后要认真做备份。我的做法很简单定期把整个~/services项目目录打包再额外备份所有命名卷。打包项目目录的命令cd /home/用户 tar czf services-backup-$(date %Y%m%d).tar.gz services命名卷备份稍微麻烦一点可以用一个临时容器来打包docker run --rm -v db_data:/data -v /home/用户/backup:/backup alpine tar czf /backup/db-$(date %Y%m%d).tar.gz -C /data .那条命令的意思是把db_data这个卷挂载到一个临时 Alpine 容器的/data目录再把 Prometheus 打包存到宿主机的/home/用户/backup目录。跑完一次/backup目录里就多了一个包含数据库全部内容的压缩包。恢复也很简单解压后的文件放回一个新卷里再启动容器。整套思路就八个字配置靠 Compose数据靠备份。3.5 更新部署的正确姿势容器化之后更新服务不再需要手动装包而是拉新镜像、重建容器。最省心的流程是这样先备份数据再修改配置文件或者更新代码然后执行docker compose pull docker compose up -dCompose 会自动对比镜像只有镜像发生变化时才重建容器。如果你的服务配置或代码通过挂载目录提供那么大多数情况下连镜像都不需要更新直接重启容器即可docker compose restart这里我要提醒一个细节每次up -d之前最好先看一眼docker ps确认当前运行的容器状态再检查一下磁盘空间。树莓派的存储通常不大想升级大镜像时如果磁盘快满了重建容器可能直接失败。4. 实操验证与性能观察4.1 一条龙实际操作流程记录为了让你有个整体观感我把从零到能访问网页的完整流程串一遍。第一步确认系统正常uname -m输出aarch64就说明是 64 位系统可以放心跑现代容器镜像。第二步更新系统sudo apt update sudo apt upgrade -y第三步装 Docker 并加权限curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER新开一个 SSH 会话验证docker version docker compose version第四步写~/services/web/docker-compose.yml放好html/index.html然后docker compose up -d第五步访问http://树莓派IP。页面出现你的内容整套流程就算跑通了。整个过程里最耗时的往往不是装软件而是“配置文件的细节”。比如 yaml 缩进错了、端口被占用了这类错误docker compose up -d会直接报错把报错信息贴出来搜一遍基本能定位。4.2 容器状态与日志怎么看跑起来以后不要只看一眼能访问就觉得完事。我建议你养成看状态和日志的习惯。docker ps正常情况下会显示所有运行中的容器状态是Up。如果容器一直重启状态会变成Restarting或者Exited。看日志docker logs webnginx的日志不会有一堆错误但如果你网站访问异常日志里会有大量线索。比如一次 502 问题日志里反复出现connect() failed (111: Connection refused) while connecting to upstream就说明 Nginx 连不上后端问题大概率出在后端服务或网络配置而不是 Nginx 本身。看资源占用docker statsdocker stats会实时显示每个容器的 CPU 和内存占用非常直观。我看过很多树莓派跑容器后“觉得卡”的帖子一问基本都是没有监控习惯。先看数据再下结论。4.3 性能、功耗与 SD 卡寿命观察树莓派 4B 跑 Nginx 静态服务空闲时 CPU 基本是 0内存占用小到可以忽略。即使跑 MySQL 和 Redis只要不是大量并发查询内存也足够。真正要担心的是 SD 卡。TF 卡寿命是树莓派长期运行的头号杀手。Docker 的日志、容器分层写入、数据库的频繁写操作都会加速 SD 卡损坏。我的经验是三条第一优先用 SSD。树莓派 4B 可以用 USB3.0 移动硬盘盒加一块 SSD 启动或者直接用树莓派 5 的 M.2 HAT 接 NVMe。我的主力树莓派已经改成 SSD 启动稳定性明显提升。第二给 Docker 加日志轮转。在/etc/docker/daemon.json里加{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }这样每个容器的日志文件超过 10MB 就会轮转最多保留 3 个旧文件防止日志把磁盘写满。第三不要频繁执行docker compose down再up。容器重建涉及大量写操作没事不要折腾它。树莓派是“少动为妙”的设备配置好以后让它安静跑着就行。4.4 局域网访问与域名映射如果只是自己电脑访问用 IP 就够了。但如果你想在手机、平板上输入一个好记的地址最简单的办法是给树莓派配一个局域网域名。两个方案。方案一是在路由器后台做hosts绑定或者 DNS 自定义把pi.local之类的名字指向树莓派 IP。方案二是用 mDNSRaspberry Pi OS 默认开启Ubuntu 需要装avahi-daemonsudo apt install avahi-daemon -y装完之后局域网里就能用树莓派主机名.local访问了。如果想把网站暴露到公网我的建议是除非你有明确需求否则不要轻易把端口开到公网。真要暴露优先用成熟的反向代理方案同时做好访问控制。不做安全加固就暴露到公网等于给全世界开了一扇门。5. 常见问题排查与避坑5.1 问题速查表下面这些是我在树莓派上跑 Docker 遇到过或者帮别人排查时解决的典型问题整理成速查表。问题现象可能原因排查方法docker run报 permission denied当前用户不在 docker 组sudo usermod -aG docker $USER后重新登录Cannot connect to the Docker daemonDocker 服务没启动systemctl status docker启动sudo systemctl start dockerport is already allocated端口被其他进程或容器占用docker ps看容器列表ss -tlnp看监听端口杀掉占用进程拉镜像超时或失败网络到 Docker Hub 不稳定检查/etc/docker/daemon.json镜像加速重启 Docker容器一直Restarting启动命令或配置有误docker logs 容器名查看崩溃日志页面 403 Forbidden挂载目录为空或权限不对确认网站根目录有index.html检查文件权限页面 502 Bad Gateway后端服务没起来或容器间网络不通docker ps确认后端容器状态检查 Compose 网络配置SD 卡空间满了镜像占满日志占满docker system df查看空间占用docker system prune清理无用资源改日志轮转docker compose不认识命令版本问题确认安装的是 compose 插件还是独立命令Compose 插件用docker compose老版本用docker-compose5.2 我踩过的几个典型坑第一个坑是 Ubuntu 22.04 下 Docker 服务假死。当时装的是 apt 仓库里非常老的docker.io包跑了两周后docker ps突然卡住重启容器也不生效最后只能重启树莓派。后来我把系统上的 Docker 彻底卸掉改用官方安装脚本问题再没出现过。经验是树莓派上的 Docker 尽量新一点老版本内核和容器运行时的兼容性确实差一些。第二个坑是挂载目录的默认行为。我第一次把空目录挂载到 Nginx 的/usr/share/nginx/html自信满满访问页面结果 403。因为空目录把镜像里的默认页面“盖住”了。解决方法是先放文件再启动容器或者先启动容器把容器内默认文件复制出来再挂载。我个人推荐前者更直接。第三个坑是镜像架构。树莓派 4B 是 ARM64但如果你手上的镜像是老旧版本可能只有 amd64 平台标签。Docker 会尝试用模拟器跑慢到怀疑人生。看到镜像内有linux/amd64说明时要留个心眼优先选支持linux/arm64的镜像。好在现在主流镜像基本都做了多架构发布这个问题越来越少。5.3 排查思路和工具排查容器问题我的顺序永远是docker ps看状态 →docker logs 容器名看日志 →docker inspect 容器名看配置详情。docker inspect是一个被低估的命令。它会把容器的完整配置、网络、挂载、环境变量全部输出成 JSON。比如两个容器是否能互相通信、端口映射是否正确、挂载路径对不对都能在这里找到答案。还有一个实用技巧是进入容器内部调试docker exec -it web sh如果镜像基于 Alpine里面只有sh没有bash这是正常的。进入容器后可以用curl、ls等基础命令检查环境。比如你怀疑 Nginx 容器里没有你的网站文件进入容器看/usr/share/nginx/html就能立刻核实。容器网络排查也经常遇到。Compose 默认会给所有服务建一个 bridge 网络服务之间用服务名互相访问。如果你改成自定义网络导致容器之间找不到可以在 Compose 文件里手动指定services: web: networks: - frontend db: networks: - frontend - backend把需要互通的容器放到同一个网络下问题就清楚了。6. 后续扩展与专栏内容导览6.1 从静态页到动态服务下一步可以做什么网页服务器跑通后延伸空间非常大。我自己的路线是先从静态站点开始然后加一个轻量动态博客再往后挂了一个简单的数据查询接口。如果你想快速体验“动态网站”推荐部署一套 Typecho 或者 Wordpress。照着 Compose 文件加一个 MySQL 服务和一个 PHP 容器把数据库连接信息通过环境变量传进去半小时内能跑起来。这类应用本身就是被 Docker 容器化得最成熟的官方镜像文档基本写得明明白白。再往后就可以尝试把更多服务和网页服务器衔接起来。比如用 Redis 做页面缓存、用定时任务生成静态页面、用内网穿透工具让外部在某些场景下也能访问。这些我都计划在后续文章里单独拆开讲尤其是“树莓派长期运行如何做监控”和“Docker 卷备份的完整方案”这两个主题值钱的部分基本都在这次专栏规划的范围内。6.2 硬件扩展方向树莓派 5 上市后PCIe 接口终于不再是摆设。接一块 M.2 HAT 加 NVMe SSD系统盘速度比 TF 卡快几个量级Docker 镜像启动和数据库读写都会有肉眼可见的提升。如果你的预算允许树莓派 5 M.2 SSD 会是未来两年玩自托管的黄金组合。回顾这篇导览对应的系列规划后续会写到的方向包括Nginx 高级配置与反向代理、MySQL 和 Redis 容器化部署细节、Docker 卷备份恢复方案、基于树莓派的智能家居与摄像头画面展示页、用 Docker 跑定时任务以及树莓派 5 的 PCIe 与 M.2 HAT 实战。6.3 我推荐的实践路线如果你是零基础建议按这个顺序推进烧录系统 → 开启 SSH → 配置网络 → 安装 Docker → 跑一个 Nginx 容器 → 用 Compose 管理一个静态网站 → 尝试加一个 MySQL 容器 → 给网站接上动态数据。每一步都确认没问题再进下一步。不要一上来就套一个包含 Nginx、PHP、MySQL、Redis、备案域名、HTTPS 的大全套因为一旦出错你连问题是出在网络、配置还是容器本身都分不清。我见过太多人一开始叠太多复杂度遇到问题直接放弃。反过来等你把静态网站跑到稳定后面做任何动态服务都只是在 Compose 文件里加几个 service路径是一样的。这个“最小闭环”一旦建立树莓派就不再是吃灰玩具而是一台真正属于你自己的网页服务器。收尾前再补两句实在话我自己的体会是树莓派跑网页服务器这件事最难的从来不是技术本身而是“别把它当成一次性项目”的心态。硬件再便宜也只是工具真正值钱的是跑在上面的数据和解决问题的能力。如果在阅读过程中遇到任何跑不通的地方先看日志再看配置最后再怀疑硬件。这条顺序能解决九成问题。最后再分享一个小技巧给树莓派配一个带智能开关的插排或者至少配一条稳定的电源线。树莓派对电压敏感劣质电源造成的随机重启、TF 卡损坏远比软件问题更让人抓狂。稳定供电、优质存储、合理的备份策略再加一份清楚的 Compose 文件它能安安静静陪你跑很久。