Self-hosted 服务搭建思路:awesome-selfhosted、Docker 与 Nginx Proxy Manager

2024 年 4 月 28 日 星期日(已编辑)
/ , , , ,
5
摘要
不罗列部署清单,而是整理 self-hosted 服务的接入流程:从 awesome-selfhosted 找候选,用 Docker Compose 管运行和数据目录,用 NPM 或手写 Nginx 管入口,管理服务放到 Zero Trust 后面。以博客、媒体自动化、AList 和管理入口作为代表例子。

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

Self-hosted 服务搭建思路:awesome-selfhosted、Docker 与 Nginx Proxy Manager

编写时间:2024-04-28

硬件、系统和网络入口都接上以后,我才开始真正往这台机器里放长期服务。这里的重点不是“我部署过哪些项目”,而是整理出一套可重复的 self-hosted 接入流程:先从需求出发找项目,再用 Docker 管运行,用 Nginx Proxy Manager 或手写 Nginx 管入口。

挑项目时可以参考 awesome-selfhosted。这个列表很适合作为索引:想做文件管理、媒体库、监控、笔记、自动化时,先看有哪些成熟项目,再回到自己的机器上判断它是否真的值得部署。

接入流程

我后面部署服务基本都按同一套流程走:

这套流程的关键是边界清晰:

负责什么不负责什么
Docker Compose服务启动、依赖、数据卷、重启策略公网入口和证书
Nginx Proxy Manager / Nginx域名反代、SSL、WebSocket、基础访问入口业务数据
Cloudflare Zero Trust管理后台访问控制普通公开内容
数据目录保存不可丢的数据保存临时容器状态

服务本身可以换,但接入方式最好稳定。这样后面新增项目时,不需要重新思考整套网络和证书。

从需求到项目

原始待办里有很多想做的事情:博客、备案首页、文件入口、媒体自动化、远程管理、个人小工具。整理成博客时,不需要把每个项目都展开,只挑几个能说明方法的例子:

需求代表例子重点问题
博客和个人主页Mix Space + Shiro前后端拆分、API 域名、CORS、双前端
媒体自动化AutoBangumi + qBittorrent + Jellyfin下载、整理、播放的目录边界
文件入口AList本地目录挂载、上传限制、公开范围
管理入口Portainer / Cockpit只能内网或受保护访问

这几个例子覆盖了大部分 self-hosted 服务会遇到的问题:有状态服务、无状态前端、容器网络、反向代理、文件挂载、管理入口保护。

Docker 基本模板

先创建一个给反向代理使用的共享网络:

docker network create nginx

每个需要通过域名访问的服务,都加入这个外部网络:

networks:
  nginx:
    external: true

一个新的 Web 服务,通常可以从这个模板开始:

services:
  app:
    image: example/app:latest
    volumes:
      - ./config:/config
      - /mnt/vault/app-data:/data
    environment:
      - TZ=Asia/Shanghai
    networks:
      - nginx
    restart: unless-stopped

networks:
  nginx:
    external: true

这里我会先问三个问题:

  1. 哪些目录是配置,哪些目录是真正的数据;
  2. 这个服务是否需要被公网访问;
  3. 如果容器删掉重建,哪些东西必须还在。

只要这三个问题想清楚,Compose 文件通常不会太乱。

NPM 入口模板

当时我选 Nginx Proxy Manager,主要是因为对 Nginx 还不够熟,面板能把域名、证书和反代先跑起来。这个选择对常规 self-hosted 服务很友好,但后来会感觉不够自由:复杂路径、缓存、header、特殊 location 还是手写 Nginx 更清楚。

如果用 NPM,反代规则尽量保持简单:

Domain Names = app.example.net
Scheme       = http
Forward Host = app
Forward Port = 8080
WebSocket    = enabled

NPM 在容器里时,localhost 指向的是 NPM 容器自己。后端服务最好加入同一个 Docker 网络,然后在 NPM 里填容器名,例如 appshiromx-server

如果要申请泛域名证书,可以用 DNS Challenge。真实凭据只放在 NPM 的凭据配置里,不进入文章、仓库或 Compose。复杂服务可以直接用 Nginx 配置接管,NPM 更适合作为起步和常规服务面板。

例子一:博客系统

博客是最典型的有状态 self-hosted 服务。后端、数据库、缓存和前端职责不同:

后端保存文章、配置和对象信息,前端只是渲染页面。这个拆法带来一个好处:后端只跑一份,前端可以有多份。例如家庭服务器跑完整后端和一个前端,固定 IP 机器只跑轻量前端,用于备案域名入口。

需要特别注意的是:

处理方式
API 域名单独给后端一个域名,例如 api.example.net
前端域名可以有多个,例如 blog.example.netexample.cn
CORS后端允许所有前端域名
后台路径和反代配置保持一致
数据层MongoDB / Redis 不暴露公网

Compose 里后端加入业务网络和 nginx 网络,数据库只留在业务网络:

services:
  mx-server:
    image: innei/mx-server:latest
    environment:
      - TZ=Asia/Shanghai
      - NODE_ENV=production
      - DB_HOST=mongo
      - REDIS_HOST=redis
      - ALLOWED_ORIGINS
      - JWT_SECRET
    volumes:
      - ./data/mx-space:/root/.mx-space
    networks:
      - mx-space
      - nginx
    restart: unless-stopped

  mongo:
    image: mongo
    volumes:
      - ./data/db:/data/db
    networks:
      - mx-space
    restart: unless-stopped

  redis:
    image: redis:alpine
    volumes:
      - ./data/redis:/data
    networks:
      - mx-space
    restart: unless-stopped

networks:
  mx-space:
  nginx:
    external: true

例子二:媒体自动化

媒体服务不是一个容器能解决的,它更像一条流水线:

这里最重要的是目录边界。下载器负责写入下载目录,Jellyfin 负责读取媒体目录,不要让播放器随便修改源文件。

services:
  jellyfin:
    image: jellyfin/jellyfin
    network_mode: host
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /mnt/vault/media:/media:ro
    devices:
      - /dev/dri:/dev/dri
    restart: unless-stopped

/dev/dri 用于硬件转码;媒体目录用只读挂载,避免误删。AutoBangumi 和 qBittorrent 之间要能互相访问,如果不在同一个 Docker 网络里,就需要确认容器内网地址或把它们接入同一网络。

例子三:文件入口

文件入口更接近 NAS 需求,但它不等同于备份系统。AList 这类服务适合聚合目录和轻量分享:

services:
  alist:
    image: xhofe/alist:latest-ffmpeg
    volumes:
      - ./data:/opt/alist/data
      - /mnt/vault/alist:/localCloud
    ports:
      - "5244:5244"
    networks:
      - nginx
    restart: unless-stopped

这里要先想清楚公开范围:哪些目录只是自己内网用,哪些可以分享给外部。大文件上传如果走 Cloudflare 入口,可能遇到上传大小限制;文件管理和 NAS 场景更适合内网、直连 DDNS,或者在自己搭好的虚拟局域网里访问。

管理入口

管理类服务不要当普通 Web 服务裸奔公网。Portainer、Cockpit、NPM 管理面板这类入口,我会优先放在 Cloudflare Application / Zero Trust 后面。

管理服务的反代不是为了公开,而是为了统一入口和认证。

小结

self-hosted 的关键不是部署越多越好,而是把每个服务都纳入同一套流程:需求明确、Docker 编排、数据目录固定、NPM 或 Nginx 反代、管理入口受保护、配置和数据可备份。

这样以后再从 awesome-selfhosted 里挑新项目时,决策成本会低很多:只要它能稳定用 Docker 跑起来,能接入 NPM 或 Nginx,数据目录和暴露边界清楚,就可以作为候选;否则先放一放。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...