Logo lkw123's Blog
我的 Self-hosting 记录

我的 Self-hosting 记录

September 27, 2024
28 min read
Table of Contents

这篇博文主要用于备份,方便自己在迁移服务时做个参考。

前景提要

我的 lkwplus.com 域名最初注册于 Namesilo,后迁移至 Cloudflare,域名解析和 CDN 由 Cloudflare 提供,项目代码托管于 GitHub(Private & Public repo)。

  • VPS:腾讯云 2C2G30M 新加坡 LightHouse 服务器、VKVM 4C8G500M 洛杉矶 9950X 高性能服务器、FameSystems 6C24G EPYC 9654 Root Server;
    • Containerized;
  • PaaS:Cloudflare (Pages/Workers)、Vercel、fly.io、Zeabur、Netlify、Heroku、Render、Koyeb、Glitch
    • Serverless;
    • Containerized;

云服务器

媒体库管理方案

在这里插一句,简要记录下我目前使用的媒体库管理方案。后面有时间可能会单独开一篇文章展开说下。

我的主要媒体资源存放于 115 网盘 (~130 TB),通过 p115dav 转换为对外的 WebDAV 服务,并使用 Clouddrive2 将 WebDAV 挂载到本地,采用 Plex 作为媒体库管理工具进行元数据刮削和影视播放。

特殊的,对于 JAV,在部署 Metatube 的后端服务后,通过安装其提供的 Plex 插件,以进行元数据的正确刮削。

个人采用 Prowlarr 管理各类 Indexer (PT/Usenet),分别对接下载器 qBittorrentSABnzbd,完成资源的下载,使用脚本采用 torcp 对资源进行符合规范的命名后上传至网盘。

Radarr(适用于电影)和 Sonarr(适用于剧集)又采用 Prowlarr 作为后端,完成对各类影视剧(已上映和未上映)的资源追踪和格式管理等,感兴趣的各位可以参考 TRaSH-Guides 实现更高级的媒体自动化请求流程管理。

相比较 Prowlarr 而言,其更多的在自动化方面派上用场,当需要进行手动资源检索时,我个人更倾向使用 NZBHydra2,如下图:

NZBHydra2

除此之外,使用 Overseerr 进行媒体库请求管理,它通过对接 Radarr 和 Sonarr,对媒体请求进行一层代理,并监控媒体请求的进程。它也能很方便展示近期热门的影视海报墙,并呈现库中是否已有该影片,从而可以便携的对未入库的影视发起请求。如下图所示:

overseerr

Tautulli 则可以对整个影视库进行追踪和监控,对各类播放数据进行整理,并自定义通知信息,如新入库的资源和新的串流等。

准备工作

安装 Docker

  1. 卸载旧版本:
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
  1. 配置 Docker apt 仓库:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
 
# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
  1. 安装最新版本 Docker:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
 
# 验证安装成功
sudo docker run hello-world

配置 Node 环境

  1. 安装 pnpm:
curl -fsSL https://get.pnpm.io/install.sh | sh -
  1. 安装 Node.js:
# LTS 版本
pnpm env use --g lts
 
# Latest 版本:
pnpm env use --g latest
  1. 安装 PM2:
pnpm install -g pm2

Caddy

文档地址,配置文件路径:/etc/caddy/Caddyfile

stable releases 安装方式:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Caddyfile 配置文件示例:

/etc/caddy/Caddyfile
# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.
{
	servers {
		metrics
	}
}
 
:2019 {
	metrics /metrics
}
 
:80 {
	# Set this path to your site's directory.
	root * /usr/share/caddy
 
	# Enable the static file server.
	file_server
 
	# Another common task is to set up a reverse proxy:
	# reverse_proxy localhost:8080
 
	# Or serve a PHP site through php-fpm:
	# php_fastcgi localhost:9000
}
 
alist.lkwplus.com {
	reverse_proxy 127.0.0.1:5244
}
# Refer to the Caddy docs for more information:
# https://caddyserver.com/docs/caddyfile

常用命令:

# 启动
systemctl start caddy
caddy start --config /etc/caddy/Caddyfile
# 关闭
systemctl stop caddy
caddy stop
# 重载配置文件
caddy reload --config /etc/caddy/Caddyfile
# 格式化 Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile

SABnzbd

Usenet 即新闻组,是一种历史久远的互联网产物,在早期主要用于论坛式的交流讨论,现在已经逐渐转化为文件存档类的服务,如想更多了解这个方向,可以在 r/usenet 等社区论坛上进行探索。

现在 Usenet 的使用体系由三部分构成:

  • Indexer(索引器):负责在 Usenet 站点上搜集并索引内容,存储在索引库中。由于很多文件在上传时会经过加密或混淆处理,没有合适的 Indexer 很难从 Usenet 上获取高质量的资源。互联网上有少数的免费 Indexer 可供选择,但质量参差不齐,往往需要付费订阅多个 Indexer 站点从而获取更高质量的资源、更多的 API Quota;
  • Provider(服务商):提供 Usenet 资源的 Post 和 Download 服务,由于 Usenet 上的资源十分庞大,各大服务商会根据自己的策略选择性地同步和存储部分内容。为了优雅的使用 Usenet 服务,往往需要付费订阅多个 Provider(不同 Backbone 的 Provider 间互相补充)来获取你所需的资源;
  • Downloader(下载器):Downloader 是传入 nzb 文件,将 Usenet 上的资源下载到本地的工具。SABnzbd 是目前使用体验最好的两个 nzb 下载器之一(另一个是 NZBGet)。

可以配合 SonarrRadarrOverseerr 等各类媒体管理工具,从而实现影视资源的自动入库和刮削,这是一个漫长的但有意思的折腾之路。

docker-compose.yml
services:
  sabnzbd:
    image: lscr.io/linuxserver/sabnzbd:latest
    container_name: sabnzbd
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
    volumes:
      - /opt/sabnzbd/config:/config
      - /opt/sabnzbd/downloads:/downloads
      - /opt/sabnzbd/tmp:/incomplete-downloads
    ports:
      - 127.0.0.1:12467:8080
    restart: unless-stopped

Alist

文档地址,安装目录: /opt/alist

# 安装
curl -fsSL "https://alist.nn.ci/v3.sh" | bash -s install
# 更新
curl -fsSL "https://alist.nn.ci/v3.sh" | bash -s update
# 卸载
curl -fsSL "https://alist.nn.ci/v3.sh" | bash -s uninstall

服务状态则通过 systemctl 管理:

# 启动
systemctl start alist
# 关闭
systemctl stop alist
# 状态
systemctl status alist
# 重启
systemctl restart alist

创建管理员密码:

/path/to/alist/alist admin set NEW_PASSWORD

对于个人而言,在配置文件 data/config.json 中修改以下内容:

data/config.json
{
  "site_url": "https://alist.lkwplus.com",
  "token_expires_in": 48,
  "scheme": {
    "address": "127.0.0.1", // 限制本机访问,外网访问通过 Caddy 反向代理
    "http_port": 5244
  }
}

添加如下存储源:

  • 坚果云:协议为 WebDAV;
  • 115 网盘:WebDAV 策略为 302 重定向,使得本地可快速访问,Cookie 则通过 Stream 软件获取自 115 官方微信小程序,有效期可达一年;
  • Cloudflare R2:对象存储,有多个 Bucket 需要添加,统一置于 /cloudflare-r2 顶层目录下;
    • 地区:不可填默认的 auto,需要根据实际情况填入 Bucket 所在区域,如 apac、wnam;
    • 自定义 host:根据个人在 Cloudflare 上为 R2 绑定的域名进行填写,如 https://img.lkwplus.com
  • 本机存储。

Dozzle

文档地址,安装目录 /opt/dozzle

Dozzle 是一个轻量的 Docker 容器日志监视器,可以实时监视 Docker 容器的日志输出,并以可视化的方式呈现,同时还支持 SQL Analytics 等,功能强大,如下图:

Dozzle 界面

首先建立数据目录 /opt/dozzle/data,然后通过 Dozzle 的 generate 命令创建 users.yaml 文件,用于 Web 界面用户鉴权:

docker run amir20/dozzle generate lkw123 --password password --email test@email.net --name "John Doe" > data/users.yml

生成的 users.yml 文件内容中记录了用户信息和经过 hash 后的密码,对于版本在 8.5.1 前的 Dozzle 实例,采用 sha-256 算法进行加密,存在安全隐患,而自 Patched version 8.5.3 起,则采用 bcrypt 算法加密密码。

data/users.yml 形如:

data/users.yml
users:
  lkw123:
    email: test@example.com
    name: lkw123
    password: $2a$11$jZEMe2xxxxx

docker-compose.yml 文件内容如下:

docker-compose.yml
services:
  dozzle:
    image: amir20/dozzle:latest
    container_name: dozzle
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /opt/dozzle/data:/data
    environment:
      - DOZZLE_AUTH_PROVIDER=simple # 开启 auth 功能
      - DOZZLE_NO_ANALYTICS=true # 关闭向 Dozzle 发送匿名统计数据
    ports:
      - 127.0.0.1:18083:8080
    restart: unless-stopped

可以在多台服务器上运行 Dozzle agent,另一台服务器上通过 DOZZLE_REMOTE_AGENT 环境变量指定 Remote Agent 的地址,即可实现多主机的日志展示,详见文档

Open WebUI

文档地址,安装目录 /opt/open-webui

Docker 安装

docker-compose.yml
services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    ports:
      - 127.0.0.1:18081:8080
    environment:
      - ENABLE_OLLAMA_API=false
      - OPENAI_API_KEY={your_secret_key}
      - OPENAI_API_BASE_URL={your_openai_api_base_url}
      - DEFAULT_MODELS=gpt-4o
      - ENABLE_MESSAGE_RATING=false
    volumes:
      - /opt/open-webui/data:/app/backend/data
    restart: unless-stopped

pip 安装

采用 uv 管理 Python 虚拟环境,安装方式如下:

curl -LsSf https://astral.sh/uv/install.sh | sh

创建虚拟环境并安装 Open WebUI:

uv venv
source .venv/bin/activate
uv pip install open-webui

创建 ecosystem.config.js 用于 pm2 进行进程管理:

ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'open-webui',
      script: '.venv/bin/open-webui',
      args: 'serve',
      cwd: '/opt/open-webui',
      interpreter: '/bin/bash',
      interpreter_args: '-c "source .venv/bin/activate && exec $0 $@"',
      error_file: './logs/open-webui-error.log',
      out_file: './logs/open-webui-out.log',
      env: {
        NODE_ENV: 'development',
      },
      env_production: {
        NODE_ENV: 'production',
      },
    },
  ],
}

启动 Open WebUI:

pm2 start ecosystem.config.js
 
# 保存状态
pm2 save
# 设置开机自启
pm2 startup

Glance

文档地址,安装目录 /opt/glance

Glance 是一个定制化程度比较高的、用于各类信息源集中展示的 dashboard。

官方给出了一个 Preconfigured page 对应的配置文件,可以先上手按照搭建出来,后面再根据自己的需求逐步修改面板的布局、功能、信息源等。配置文件路径为:/opt/glance/glance.yml

docker-compose.yml
services:
  glance:
    image: glanceapp/glance:latest
    container_name: glance
    volumes:
      - /opt/glance/glance.yml:/app/glance.yml
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - 127.0.0.1:13242:8080
    restart: unless-stopped

n8n

n8n 可用于构建自动化工作流,是 IFTTT 的开源替代,有各类软件集成,通过 Web UI 可以很方便的对自动化流程进行编辑与配置。文档地址

docker-compose.yml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=host.docker.internal
      - DB_POSTGRESDB_PORT=5444
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=<PASSWORD>
    ports:
      - 127.0.0.1:15678:5678
    volumes:
      - /opt/n8n/n8n-storage:/home/node/.n8n
    extra_hosts:
      - host.docker.internal:host-gateway

下面介绍的是一个简单的 Grafana + Prometheus + blabla-exporter + cAdvisor 构建的个人服务器监控体系:

Node Exporter

参考官方的 GitHub README,照抄配置文件即可。

Node Exporter 和下面的 Postgres Exporter 均采用 host 模式,直接使用宿主机的网络,从而便携的对相关资源进行监控。

docker-compose.yml
services:
  node_exporter:
    image: quay.io/prometheus/node-exporter:latest
    container_name: node_exporter
    command:
      - '--path.rootfs=/host'
    network_mode: host
    pid: host
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'

Postgres Exporter

用于监控 PostgreSQL 数据库的 Exporter。

docker-compose.yml
services:
  postgres-exporter:
    image: quay.io/prometheuscommunity/postgres-exporter:latest
    container_name: postgres_exporter
    environment:
      DATA_SOURCE_URI: 'localhost:5444/postgres?sslmode=disable'
      DATA_SOURCE_USER: '{your_username}'
      DATA_SOURCE_PASS: '{your_password}'
    network_mode: host
    restart: unless-stopped

cAdvisor

监控容器资源可以有多种方案,这里采用 cAdvisor,它是一个运行在 Docker 容器内的、提供容器资源监控的工具。GitHub README

由于 Prometheus 实例和 cAdvisor 实例间通信通过 docker network 内部完成,因此可以省略掉 ports 配置。

docker-compose.yml
services:
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    privileged: true
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    devices:
      - /dev/kmsg
    command:
      - '--docker_only=true'
      - '--housekeeping_interval=10s'
      - '--disable_metrics=disk,udp,referenced_memory,cpu_topology,resctrl,tcp,advtcp,sched,process,hugetlb,percpu'
    restart: unless-stopped
    networks:
      - monitoring
 
networks:
  monitoring:
    external: true

Beszel

Beszel 是一个轻量级的服务器监控工具,功能强大、前端界面美观,支持 Docker 监控和告警等。暂时选用 Beszel 作为容器资源监控的方案,替换掉了之前的 cAdvisor。GitHub README

docker-compose.yml
services:
  beszel:
    image: henrygd/beszel:latest
    container_name: beszel
    restart: unless-stopped
    ports:
      - 127.0.0.1:18096:8090
    volumes:
      - /opt/beszel/beszel_data:/beszel_data
    extra_hosts:
      - host.docker.internal:host-gateway

进入页面后会要求设置初始管理员账户,添加需要进行监控的主机的相关 IP、端口信息,Beszel 会自动生成 agent 部署所需的 docker-compose.yml。在放行相关端口的防火墙策略后,便可在目标主机启动 agent 开启监控。

Prometheus

为了使得 Prometheus 实例和 Grafana、cAdvisor 实例间顺畅通信,可以创建一个 docker network,命名为 monitering,并在相关的 docker-compose 配置文件中做好相关的定义,使其加入到这个 network 中。

同时,为了使得 Promethus 可以向 Node Exporter 和 Postgres Exporter 等采集数据,可以配置 extra_hosts 选项,将宿主机的 IP 地址映射到容器内的 host.docker.internal

docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    hostname: prometheus
    restart: unless-stopped
    volumes:
      - /opt/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - /opt/prometheus/data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--web.enable-lifecycle'
    ports:
      - 127.0.0.1:19090:9090
    networks:
      - monitoring
    extra_hosts:
      - host.docker.internal:host-gateway
 
networks:
  monitoring:
    external: true

同时,通过 Prometheus 的配置文件 prometheus.yml,定义各类 job,如下:

prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
 
scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']
        labels:
          instance: prometheus
  - job_name: node
    static_configs:
      - targets: ['host.docker.internal:9100']
  - job_name: postgres
    static_configs:
      - targets: ['host.docker.internal:9187']
  - job_name: caddy
    static_configs:
      - targets: ['host.docker.internal:2019']
  - job_name: cadvisor
    scrape_interval: 30s
    static_configs:
      - targets: ['cadvisor:8080']

共定义了五个 job,每个 job 后续在 Grafana 对应一个 Dashboard,展示相关数据:

  • prometheus:Prometheus 自身的监控指标,默认对外提供服务于 http://localhost:9090

  • node:Node Exporter 采集的主机资源数据,由于采用 host 模式,可以通过 http://host.docker.internal:9100 访问宿主机的 9100 端口获取数据;

  • postgres:Postgres Exporter 采集的数据库资源数据,由于采用 host 模式,可以通过 http://host.docker.internal:9187 访问宿主机的 9187 端口获取数据;

  • caddy:Caddy 自身提供 Metrics 获取接口以供 Prometheus 采集,需要在 Caddyfile 中进行配置,开启 Metrics 接口:

    /etc/caddy/Caddyfile
    {
      servers {
        metrics
      }
    }
     
    :2019 {
      metrics /metrics
    }
  • cadvisor:cAdvisor 采集的容器资源数据,由于 Prometheus 和 cAdvisor 在同一个 docker network 中,因此通过 http://cadvisor:8080 可以访问到。

VictoriaMetrics

经过对比,由 Prometheus 切换至单节点部署的 VictoriaMetrics,在存储空间、性能、系统资源占用方面都有明显提升。需要部署两个容器:

  • victoria-metrics:用于存储和查询时间序列数据,采用单机模式部署;
  • vmagent:用于从数据源抓取信息,并将其上报到 VictoriaMetrics。

docker-compose.yml 文件内容如下:

docker-compose.yml
services:
  victoriametrics:
    image: victoriametrics/victoria-metrics:latest
    container_name: victoriametrics
    hostname: victoriametrics
    restart: unless-stopped
    volumes:
      - /opt/victoriametrics/data:/victoria-metrics-data
    command:
      - '--storageDataPath=/victoria-metrics-data'
      - '--httpListenAddr=:8428'
      - '--retentionPeriod=1'
    ports:
      - 127.0.0.1:18428:8428
    networks:
      - monitoring
    extra_hosts:
      - host.docker.internal:host-gateway
 
  vmagent:
    image: victoriametrics/vmagent:latest
    container_name: vmagent
    hostname: vmagent
    restart: unless-stopped
    volumes:
      - /opt/victoriametrics/vmagent.yml:/etc/vmagent/vmagent.yml
    command:
      - '--promscrape.config=/etc/vmagent/vmagent.yml'
      - '--remoteWrite.url=http://victoriametrics:8428/api/v1/write'
    networks:
      - monitoring
    depends_on:
      - victoriametrics
    extra_hosts:
      - host.docker.internal:host-gateway
 
networks:
  monitoring:
    external: true

配置文件 vmagent.yml 可与此前的 prometheus.yml 基本兼容,部分不支持的字段会默认跳过不进行严格校验。

下面简要描述为 vmagentVictoriaMetrics 配置 Basic Auth 认证的过程。

  • 修改 docker-compose.yml,在各 service 的 command 部分添加命令行参数:
    • 对于 VictoriaMetrics,添加 --httpAuth.username={user1}--httpAuth.password={pwd1} 用于自身的身份认证;
    • 对于 vmagent,添加 --remoteWrite.basicAuth.username={user1}--remoteWrite.basicAuth.password={pwd1},用于 remote write 推送数据时进行认证;添加 --httpAuth.username={user2}--httpAuth.password={pwd2} 用于自身的身份认证;
  • 修改 vmagent.yml,为 vmagent 从自身拉取 metrics 指标时添加认证:
    /opt/victoriametrics/vmagent.yml
    scrape_configs:
      - job_name: victoriametrics
        basic_auth:
          username: '{user1}'
          password: '{pwd1}'
        static_configs:
          - targets: ['victoriametrics:8428']
      - job_name: vmagent
        basic_auth:
          username: '{user2}'
          password: '{pwd2}'
        static_configs:
          - targets: ['localhost:8429']
  • 在 Grafana 中配置数据源时选择 Prometheus,添加 VictoriaMetrics 实例,并添加 Basic Auth 认证信息。

认证会在三个阶段进行:

  • 访问 vmagent 和 VictoriaMetrics 的 WebUI;
  • vmagent 向 VictoriaMetrics 进行 remote write 推送数据指标;
  • Grafana 对接 VictoriaMetrics 进行数据查询并展示为图表。

Grafana

安装 Grafana 时会默认将数据存储于 sqlite3。考虑到性能和高可用等因素,可以选用 Postgres 等作为数据库,在常规的 Docker Compose 配置文件中加入如下环境变量即可:

docker-compose.yml
- GF_DATABASE_TYPE=postgres
- GF_DATABASE_HOST=host.docker.internal:5444
- GF_DATABASE_NAME=grafana
- GF_DATABASE_USER=grafana
- GF_DATABASE_PASSWORD={your_password}
- GF_DATABASE_SSL_MODE=disable

docker-compose.yml 文件内容如下:

docker-compose.yml
services:
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    environment:
      - GF_SERVER_ROOT_URL=https://grafana.lkwplus.com/
    extra_hosts:
      - host.docker.internal:host-gateway
    ports:
      - 127.0.0.1:19823:3000
    volumes:
      - /opt/grafana/data:/var/lib/grafana
    networks:
      - monitoring
 
networks:
  monitoring:
    external: true

对应 Prometheus 中的五个 job,在 Grafana 中添加如下 Dashboard,用于不同类别的监控信息展示:

Gitea

一个基于 Go 语言开发的轻量级的 Git 服务器,文档地址

docker-compose.yml
services:
  server:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=host.docker.internal:5444
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD={your_password}
    restart: unless-stopped
    extra_hosts:
      - host.docker.internal:host-gateway
    volumes:
      - /opt/gitea/data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - 127.0.0.1:13099:3000
      - 222:22

qBittorrent

安装目录 /opt/qbittorrentdocker-compose.yml 文件内容如下:

docker-compose.yml
services:
  qbittorrent:
    image: linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - TZ=Asia/Shanghai
      - UMASK_SET=022
      - WEBUI_PORT=8081
    volumes:
      - /opt/qbittorrent/config:/config
      - /opt/qbittorrent/downloads:/downloads
    ports:
      - 65231:65231
      - 65231:65231/udp
      - 127.0.0.1:8081:8081
    restart: unless-stopped

your_spotify

一个很美观的 Spotify 听歌记录展示页面,项目地址,由三部分组成:

  • your_spotify_server:后端服务,负责提供 API 接口;
  • your_spotify_client:前端客户端,负责展示数据;
  • mongodb:6:数据库,负责存储数据。

为了节省资源,我省去了部署 MongoDB 的步骤,使用的是来自外部的免费 MongoDB 实例,通过 MONGO_ENDPOINT 环境变量进行声明。

值得注意的是,在部署完毕后 Web 端和服务端一直无法进行通信导致无法 Auth,最终解决方案是在 Cloudflare 域名解析中将 API_ENDPOINT 的 Proxy Status 设置为 DNS only(即关闭小黄云)。

在管理面板创建 token 后,可以将自己的 Spotify 数据分析面板分享给访客,示例效果

docker-compose.yml
services:
  server:
    image: yooooomi/your_spotify_server:latest
    container_name: your_spotify_server
    restart: unless-stopped
    ports:
      - 127.0.0.1:18777:8080
    environment:
      API_ENDPOINT: https://your-spotify.lkwplus.com
      CLIENT_ENDPOINT: https://spotify.lkwplus.com
      TIMEZONE: Asia/Shanghai
      SPOTIFY_PUBLIC: { your_spotify_client_id }
      SPOTIFY_SECRET: { your_spotify_secret }
      MONGO_ENDPOINT: mongodb+srv://lkw123:{password}@cluster0.kopgi.mongodb.net/your_spotify?retryWrites=true&w=majority&appName=Cluster0
      MONGO_NO_ADMIN_RIGHTS: true
      CORS: i-want-a-security-vulnerability-and-want-to-allow-all-origins
 
  web:
    image: yooooomi/your_spotify_client:latest
    container_name: your_spotify_client
    restart: unless-stopped
    ports:
      - 127.0.0.1:13241:3000
    environment:
      API_ENDPOINT: https://your-spotify.lkwplus.com

Opengist

GitHub Gist 的开源替代,项目地址

docker-compose.yml
services:
  opengist:
    image: ghcr.io/thomiceli/opengist:1
    container_name: opengist
    restart: unless-stopped
    ports:
      - 127.0.0.1:16157:6157 # Web
      - 2222:2222 # SSH
    volumes:
      - /opt/opengist/data:/opengist

Hoarder

此前一直使用 Omnivore 作为稍后读工具,存档一些值得阅读学习的文章、项目链接等,而近期该项目宣布停止服务,于是便开始寻找一款合适的开源自部署的「书签、网页存档、稍后读」工具。

在搜寻了一些社区的讨论后,通过筛选 selfh.st 中标签为 Bookmarks 的项目,了解到 LinkwardenReadeckLinkding 等活跃的社区项目替代品。

我比较看重经由接入 OpenAI API 自动根据网页内容生成标签的功能,选择 Hoarder 作为新的稍后读工具。安装部署流程详见 官方文档

Redis

安装目录 /opt/redisdocker-compose.yml 文件内容如下:

docker-compose.yml
services:
  redis:
    image: redis:latest
    container_name: redis
    restart: unless-stopped
    ports:
      - 6479:6379
    volumes:
      - /opt/redis/data:/data
      - /opt/redis/redis.conf:/usr/local/etc/redis/redis.conf
      - /opt/redis/logs:/logs
    command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes --requirepass '{your_password}'

Vikunja

Vikunja 是一个开源的 Todo 应用,整体体验还不错,但目前功能有一定的局限性,例如在 iOS 上工作不正常、无法离线使用等,后续可能会寻找其他 Todo 类的开源替代。

后端数据库可选用 mariadb、postgres、sqlite 等,docker-compose.yml 文件内容如下:

docker-compose.yml
services:
  vikunja:
    image: vikunja/vikunja:latest
    container_name: vikunja
    environment:
      VIKUNJA_SERVICE_PUBLICURL: https://todo.lkwplus.com
      VIKUNJA_DATABASE_HOST: vikunja_db
      VIKUNJA_DATABASE_PASSWORD: { db_password }
      VIKUNJA_DATABASE_TYPE: postgres
      VIKUNJA_DATABASE_USER: vikunja
      VIKUNJA_DATABASE_DATABASE: vikunja
      VIKUNJA_SERVICE_JWTSECRET: { jwt_secret }
      VIKUNJA_SERVICE_TIMEZONE: Asia/Shanghai
      VIKUNJA_DEFAULTSETTINGS_WEEK_START: 1 # 0 表示周日;1 表示周一
      VIKUNJA_SERVICE_ENABLEREGISTRATION: true
    ports:
      - 127.0.0.1:13456:3456
    volumes:
      - /opt/vikunja/files:/app/vikunja/files
    depends_on:
      vikunja_db:
        condition: service_healthy
    restart: unless-stopped
 
  vikunja_db:
    image: postgres:16
    container_name: vikunja_db
    environment:
      POSTGRES_PASSWORD: { db_password }
      POSTGRES_USER: vikunja
    volumes:
      - vikunja_db:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -h localhost -U $$POSTGRES_USER']
      interval: 10s
 
volumes:
  vikunja_db:

Changedetection.io

Changedetection.io 是一个开源的网页变更监控工具,可以监控网页内容的变化并及时通知。它支持多种监控方式,包括文本内容、CSS 选择器、JSON 数据等,还可以设置过滤规则来忽略不需要关注的变化。可用于监测博客文章更新或是 VPS 商家补货等。

需要注意的是,Changedetection 通过 apprise 实现消息的实时通知,例如 Telegram Bot 的通知方式可定义为 tgram://bottoken/ChatID

docker-compose.yml
services:
  changedetection:
    image: dgtlmoon/changedetection.io:latest
    container_name: changedetection
    hostname: changedetection
    volumes:
      - changedetection_data:/datastore
    environment:
      - PUID=1000
      - PGID=1000
      - BASE_URL=https://changedetection.lkwplus.com
      - DISABLE_VERSION_CHECK=true
      - TZ=Asia/Shanghai
    ports:
      - 127.0.0.1:15000:5000
    restart: unless-stopped
 
volumes:
  changedetection_data:

SFTPGo

SFTPGo 是一个功能强大的 SFTP 服务器,可以通过界面美观简洁的 WebUI 进行用户管理和文件操作,支持 FTP、SFTP、SCP、WebDAV 等多种协议,并支持多种认证方式。

docker-compose.yml
services:
  sftpgo:
    image: drakkan/sftpgo:alpine
    container_name: sftpgo
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 127.0.0.1:18080:8080 # WebUI
      - 8090:8090
      - 2022:2022
    volumes:
      - /opt/sftpgo/data:/srv/sftpgo
      - /opt/sftpgo/config:/var/lib/sftpgo
    restart: unless-stopped

PG Back Web

PG Back Web 是一个开源的 PostgreSQL 备份管理工具,可以通过 WebUI 进行备份管理,将数据库定期备份于本地和 S3 等对象存储。安装部署流程详见 GitHub README。类似的解决方案还有 Docker PG Backup 等。

WeWeRSS

文档地址,用于将微信公众号转为 RSS Feed,体验还不错,订阅了一些技术类的公众号。

docker-compose.yml
services:
  app:
    image: cooderl/wewe-rss-sqlite:latest
    container_name: wewerss
    ports:
      - 127.0.0.1:4005:4000
    environment:
      # 数据库连接地址
      # - DATABASE_URL=file:../data/wewe-rss.db
      - DATABASE_TYPE=sqlite
      # 服务接口请求授权码
      # - AUTH_CODE=0211
      # 提取全文内容模式
      # - FEED_MODE=fulltext
      # 定时更新订阅源Cron表达式
      # - CRON_EXPRESSION=35 5,17 * * *
      # 服务接口请求限制,每分钟请求次数
      # - MAX_REQUEST_PER_MINUTE=60
      # 外网访问时,需设置为服务器的公网 IP 或者域名地址
      - SERVER_ORIGIN_URL=https://wewerss.lkwplus.com
    restart: unless-stopped
    volumes:
      # 映射数据库文件存储位置,容器重启后不丢失
      - /opt/wewerss/data:/app/data

Moments

极简朋友圈风格的个人站点,用来无聊的时候瞎哔哔几句,项目地址

docker-compose.yml
services:
  moments:
    image: kingwrcy/moments:latest
    container_name: moments
    ports:
      - 127.0.0.1:16237:3000
    volumes:
      - /opt/moments/data:/app/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    restart: unless-stopped

Remark42

用于博客评论,项目地址,和其它主流评论系统相比,Remark42 该有的功能都不缺,整体体验还不错,支持登录的社交媒体也相当全面。

若不方便部署在自己的服务器,也可以参考博文 使用 Remark42 和 Fly.io 搭建免费评论系统 选择部署在 Fly.io 上。

docker-compose.yml
services:
  remark:
    image: umputun/remark42:latest
    container_name: remark42
    hostname: remark42
    restart: unless-stopped
    logging:
      driver: json-file
      options:
        max-size: '10m'
        max-file: '5'
    ports:
      - 127.0.0.1:18431:8080
    environment:
      - TIME_ZONE=Asia/Shanghai
      - REMARK_URL=https://remark42.lkwplus.com
      - SECRET={your_secret} # openssl rand -hex 16
      - SITE=blog
      - ADMIN_SHARED_ID={your_admin_shared_id}
      - AUTH_GITHUB_CID={your_github_client_id}
      - AUTH_GITHUB_CSEC={your_github_client_secret}
      - AUTH_GOOGLE_CID={your_google_client_id}
      - AUTH_GOOGLE_CSEC={your_google_client_secret}
      - AUTH_DISCORD_CID={your_discord_client_id}
      - AUTH_DISCORD_CSEC={your_discord_client_secret}
      - AUTH_TELEGRAM=true
      - TELEGRAM_TOKEN={your_telegram_bot_token}
      - NOTIFY_ADMINS=telegram
      - NOTIFY_TELEGRAM_CHAN={your_telegram_channel_id}
    volumes:
      - /opt/remark42/var:/srv/var

PocketBase

PocketBase 采用 Go 语言开发,是一个开源的实时后端解决方案,可以很方便的作为 Web 和移动应用的后端。

它集成了嵌入式 SQLite 数据库、实时数据订阅、用户管理、文件存储和管理仪表板等功能,可以做到开箱即用。

docker-compose.yml
services:
  pocketbase:
    image: ghcr.io/muchobien/pocketbase:latest
    container_name: pocketbase
    restart: unless-stopped
    command:
      - --encryptionEnv # optional
      - ENCRYPTION # optional
    environment:
      ENCRYPTION: { your_encryption_key } # openssl rand -hex 16
    ports:
      - 127.0.0.1:16487:8090
    volumes:
      - /opt/pocketbase/data:/pb_data
      - /opt/pocketbase/public:/pb_public # optional
      - /opt/pocketbase/hooks:/pb_hooks # optional
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:8090/api/health || exit 1
      interval: 10s
      timeout: 5s
      retries: 5

Wallos

Wallos 是一个开源的个人订阅项目追踪器,可以很方便的记录如 Netflix、Spotify 等订阅项目的价格、到期时间等,有美观的图标展示,并支持多种通知方式,但美中不足的是对国内支付方式较少。

docker-compose.yml
services:
  wallos:
    container_name: wallos
    image: bellamy/wallos:latest
    ports:
      - 127.0.0.1:18289:80/tcp
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /opt/wallos/db:/var/www/html/db
      - /opt/wallos/logos:/var/www/html/images/uploads/logos
    restart: unless-stopped

code-server

code-server 是一个开源的 Web 代码编辑器,它将 VS Code 的核心功能移植到服务器端,用户可以通过任何设备上的浏览器访问,从而很方便的进行代码编辑和调试。

docker-compose.yml
services:
  code-server:
    image: lscr.io/linuxserver/code-server:latest
    container_name: code-server
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
      - HASHED_PASSWORD={your_hashed_password} # 网页登录密码
      - SUDO_PASSWORD={your_sudo_password} # 终端提权密码
      - PROXY_DOMAIN={your_domain}
      - DEFAULT_WORKSPACE=/config/workspace
    volumes:
      - /opt/code-server/config:/config
    ports:
      - 127.0.0.1:18453:8443
    restart: unless-stopped

Uptime-Kuma

用于监控各类网站、服务等的可用性,并即时通知,文档地址

docker-compose.yml
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - 127.0.0.1:13059:3001
    volumes:
      - /opt/uptime-kuma/data:/app/data
      - /var/run/docker.sock:/var/run/docker.sock # 用于监控 Docker 容器状态

Watchtower

文档地址,用于(自动)更新 Docker 镜像。

# 拉取镜像
docker pull containrrr/watchtower
# 执行手动更新
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower -cR {CONTAINER_NAME}

目前使用其用于每天自动更新镜像,并使用 Telegram Bot 进行通知。

对于不希望被 Watchtower 接管进行自动更新的 Container(例如 Immich),在相应的配置文件中添加 labels "com.centurylinklabs.watchtower.enable=false" 即可。参考文档

docker-compose.yml
services:
  watchtower:
    image: containrrr/watchtower:latest
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: container1 container2 container3 # 可选,仅更新指定容器
    environment:
      - TZ=Asia/Shanghai
      - WATCHTOWER_SCHEDULE=0 0 17 * * *
      - WATCHTOWER_NOTIFICATIONS_HOSTNAME={your_hostname}
      - WATCHTOWER_NOTIFICATIONS=shoutrrr
      - WATCHTOWER_NOTIFICATION_URL=telegram://{HTTP_API_TOKEN}@telegram/?chats={CHAT_ID}&notification=no
      - WATCHTOWER_CLEANUP=true

Diun

Diun (Docker Image Update Notifier) 是一个 Docker 镜像更新通知工具,暂时未使用,由 Watchtower 替代。

docker-compose.yml
services:
  diun:
    image: crazymax/diun:latest
    container_name: diun
    command: serve
    volumes:
      - /opt/diun/data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - 'TZ=Asia/Shanghai'
      - 'DIUN_WATCH_WORKERS=20'
      - 'DIUN_WATCH_SCHEDULE=0 */6 * * *'
      - 'DIUN_WATCH_JITTER=30s'
      - 'DIUN_PROVIDERS_DOCKER=true'
      - 'DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true'
      - 'DIUN_NOTIF_TELEGRAM_TOKEN={your_bot_token}'
      - 'DIUN_NOTIF_TELEGRAM_CHATIDS={your_chat_id}'
    labels:
      - 'diun.enable=true'
    restart: unless-stopped

Restic

增量备份方案,文档地址,类似的项目如 Duplicati (Linux 下运行需要 mono,所以排除)、Borg 等,详见 ArchLinux Wiki

export AWS_DEFAULT_REGION={your_region}
export AWS_ACCESS_KEY_ID={your_access_key}
export AWS_SECRET_ACCESS_KEY={your_secret_key}
export RESTIC_PASSWORD={your_restic_password}
export RESTIC_REPOSITORY=s3:https://{your_endpoint}.r2.cloudflarestorage.com/{your_bucket_name}
restic init

首先 dry-run 测试可以正常进行备份:

restic backup /which/path/to/backup --dry-run -vv

可以将备份任务添加至 crontab,如:

30 4 * * * /usr/bin/restic backup /which/path/to/backup --password-file /path/to/rc-password >> /path/to/backup.log 2>&1

资源使用情况

腾讯云

腾讯云的新加坡 Lighthouse 实例,由于内存较小,很多项目的部署会比较吃紧,但对大陆的延迟相对较低,因此主要用于部署 Umami 和 New-API,内存使用情况大致如下:

$ free -h
               total        used        free      shared  buff/cache   available
Mem:           1.8Gi       919Mi       115Mi        16Mi       1.0Gi       919Mi
Swap:          2.0Gi       307Mi       1.7Gi
NAMECPU %MEM USAGE / LIMITMEM %NET I/OBLOCK I/OPIDS
dozzle-agent0.10%9.574MiB / 1.796GiB0.52%34.6kB / 31.5kB12.3kB / 86kB8
umami-db0.01%39.94MiB / 1.796GiB2.17%351kB / 282kB13.7MB / 14.1MB11
new-api0.00%84.17MiB / 1.796GiB4.58%10MB / 6.27MB30.5MB / 17MB7
beszel-agent0.00%8.172MiB / 1.796GiB0.44%0B / 0B3.24MB / 2.15MB7
watchtower0.00%23.87MiB / 1.796GiB1.30%3.56MB / 1.21MB16.9MB / 4.53MB8
wewerss0.00%60.34MiB / 1.796GiB3.28%28.7MB / 125MB78.9MB / 165MB15
umami5.67%127.3MiB / 1.796GiB6.92%24.6MB / 81.6MB198MB / 155MB49
nezha0.18%32.3MiB / 1.796GiB1.76%6.83GB / 4.44GB164MB / 146MB11
node_exporter0.00%14.85MiB / 1.796GiB0.81%0B / 0B39.4MB / 12MB5

FameSystems

对于目前的主力机 (FameSystems),内存使用情况大致如下:

$ free -h
               total        used        free      shared  buff/cache   available
Mem:            23Gi       8.0Gi       542Mi       401Mi        15Gi        15Gi
Swap:          1.0Gi       224Mi       799Mi
NameCPU %MEM USAGE / LIMITMEM %NET I/OBLOCK I/OPIDS
maybe_app0.06%153.8MiB / 23.47GiB0.64%5.57MB / 7.92MB9.84MB / 881kB16
p115dav0.22%50.2MiB / 23.47GiB0.21%644kB / 168kB1.54MB / 1.37MB4
dozzle0.00%16.5MiB / 23.47GiB0.07%513kB / 35.6kB0B / 0B11
remark420.03%27.12MiB / 23.47GiB0.11%140MB / 35.2MB4.01MB / 28.7MB13
postgres0.01%47.12MiB / 23.47GiB0.20%26.3MB / 29.9MB7.46MB / 8.95MB10
nextcloud-aio-mastercontainer0.55%62.62MiB / 23.47GiB0.26%2.06MB / 82.1kB283kB / 102kB152
metatube-postgres0.01%16.8MiB / 23.47GiB0.07%1.71MB / 0B168kB / 32.8kB6
pocketbase0.00%29.66MiB / 23.47GiB0.12%2.88MB / 2.34MB94.2kB / 103MB12
homepage0.00%112.7MiB / 23.47GiB0.47%3.47MB / 962kB26MB / 90.1kB11
syncthing0.03%38.25MiB / 23.47GiB0.16%6.38MB / 3.11MB270kB / 303kB24
beszel-agent0.00%10.83MiB / 23.47GiB0.05%0B / 0B0B / 0B9
beszel0.02%41.73MiB / 23.47GiB0.17%25.3MB / 11.6MB1.32MB / 475MB12
hoarder_web0.37%324.3MiB / 23.47GiB1.35%6.76MB / 6.45MB24.9MB / 13.2MB63
open-webui0.20%570.9MiB / 23.47GiB2.38%4.73MB / 7.14MB17.5MB / 41.9MB19
qinglong0.83%346.6MiB / 23.47GiB1.44%24.2MB / 27.7MB89MB / 1.11MB64
plex0.22%343.8MiB / 23.47GiB1.43%0B / 0B27MB / 1.37GB56
thelounge0.00%139.6MiB / 23.47GiB0.58%52.1MB / 24.7MB74.4MB / 6.22GB11
nzbhydra20.03%549.9MiB / 23.47GiB2.29%14.3MB / 105kB24.3MB / 6.28MB33
victoriametrics0.23%172.5MiB / 23.47GiB0.72%1.1GB / 108MB167MB / 1.68GB13
filecodebox0.39%62.02MiB / 23.47GiB0.26%7.11MB / 1.57MB2.44MB / 1.35MB4
clouddrive20.18%54.7MiB / 23.47GiB0.23%0B / 0B40.7MB / 530MB39
sabnzbd0.04%52.88MiB / 23.47GiB0.22%6.76MB / 11.3MB147kB / 459kB30
metatube0.00%119.9MiB / 23.47GiB0.50%815MB / 230MB47.2MB / 56.5MB10
vmagent0.13%40.55MiB / 23.47GiB0.17%565MB / 1.14GB11.3MB / 483kB13
immich_server0.06%506.7MiB / 23.47GiB2.11%705MB / 1.51GB50.7MB / 156MB28
immich_machine_learning0.21%297.3MiB / 23.47GiB1.24%9.96MB / 67.8kB438MB / 11.2MB15
changedetection0.08%206.2MiB / 23.47GiB0.86%35.3MB / 911kB17MB / 4.18MB14
glance0.00%15.75MiB / 23.47GiB0.07%10.9MB / 3.12MB2.06MB / 41kB8
code-server0.00%70MiB / 23.47GiB0.29%7.01MB / 464kB2.87MB / 451kB32
vikunja_db0.00%22.5MiB / 23.47GiB0.09%28.7MB / 23.5MB500kB / 627kB6
maybe_postgres0.60%29.7MiB / 23.47GiB0.12%120MB / 72.3MB4.09MB / 378MB8
vikunja0.00%28.09MiB / 23.47GiB0.12%30.7MB / 26.7MB21MB / 127kB12
wallos0.01%21.84MiB / 23.47GiB0.09%8.58MB / 4.51MB4.78MB / 11.6MB8
uptime-kuma0.37%147.8MiB / 23.47GiB0.62%6.72MB / 91.3kB1.41MB / 9.72GB19
nextcloud-aio-apache0.03%45.82MiB / 23.47GiB0.19%51.6MB / 58.5MB9.32MB / 24.6kB124
nextcloud-aio-whiteboard0.01%67.53MiB / 23.47GiB0.28%42MB / 46.1MB2.02MB / 0B23
nextcloud-aio-notify-push0.01%4.363MiB / 23.47GiB0.02%9.67MB / 5.05MB565kB / 0B9
nextcloud-aio-nextcloud0.03%97.1MiB / 23.47GiB0.40%346MB / 260MB6.05MB / 4.59MB8
nextcloud-aio-imaginary0.01%13.31MiB / 23.47GiB0.06%6.72MB / 0B1.53MB / 0B14
nextcloud-aio-redis0.22%14.67MiB / 23.47GiB0.06%118MB / 65.9MB6.05MB / 1.12GB7
nextcloud-aio-database0.00%195.9MiB / 23.47GiB0.82%172MB / 281MB3.69MB / 3.26GB8
nextcloud-aio-collabora0.09%505.4MiB / 23.47GiB2.10%80.9MB / 22.1MB848kB / 438MB13
n8n0.03%158.6MiB / 23.47GiB0.66%19.2MB / 22.4MB35.8MB / 4.1kB13
moments0.00%14.28MiB / 23.47GiB0.06%7.54MB / 21.9MB3.59MB / 20.5kB10
tautulli0.10%123.3MiB / 23.47GiB0.51%42.7MB / 9.87MB13.1MB / 121MB32
overseerr0.03%203.5MiB / 23.47GiB0.85%289MB / 72.6MB31.3MB / 73MB23
autobrr0.00%48.61MiB / 23.47GiB0.20%25.4MB / 13.8MB29MB / 2.78MB12
opengist0.00%32.86MiB / 23.47GiB0.14%7.23MB / 11.2MB11.1MB / 61.4kB12
gitea0.14%126.6MiB / 23.47GiB0.53%103MB / 104MB18.8MB / 10.7MB16
your_spotify_client0.00%23.69MiB / 23.47GiB0.10%6.9MB / 85.6kB8.65MB / 16.4kB12
your_spotify_server0.02%97.27MiB / 23.47GiB0.40%355MB / 66.4MB13.7MB / 16.4kB23
qbittorrent0.08%20.98MiB / 23.47GiB0.09%567MB / 281MB9.1MB / 463kB17
calibre-web-automated0.01%164MiB / 23.47GiB0.68%6.98MB / 120kB9.21MB / 8.22MB26
grafana0.08%99.63MiB / 23.47GiB0.41%91MB / 68MB34.1MB / 451kB12
hoarder_chrome0.04%67.88MiB / 23.47GiB0.28%8.15MB / 1.51MB4.62MB / 1.64MB45
hoarder_meilisearch0.11%99.31MiB / 23.47GiB0.41%6.66MB / 8.12kB33.4MB / 25.6MB19
sftpgo0.00%38.75MiB / 23.47GiB0.16%24.1MB / 18.9MB20.5MB / 115kB12
immich_postgres0.00%133MiB / 23.47GiB0.55%15.1MB / 312MB61.8MB / 1.55GB22
immich_redis0.18%12.34MiB / 23.47GiB0.05%1.48GB / 358MB4.49MB / 351MB5
node_exporter0.00%31.16MiB / 23.47GiB0.13%0B / 0B11.8MB / 36.9kB5
watchtower0.00%32.24MiB / 23.47GiB0.13%11.3MB / 1.66MB13MB / 53.2kB12

TODO

  • 重要数据定期备份方案,上传至 Cloudflare R2;—> Restic
  • 容器镜像的自动更新方案,通过 Telegram Bot 通知;—> Watchtower

Cloudflare

可以参考 Awesome Cloudflare,发现更多有意思的基于 Cloudflare 的开源工具。

Cloudflare Pages

ChatGPT-Next-Web

调用各类 LLM api 进行对话的网页端项目,文档地址,配置构建命令和输出目录如下:

  • Build command: npx @cloudflare/next-on-pages --experimental-minify
  • Build output: .vercel/output/static

配置如下环境变量:

CODE = "{your_code}" # 网页端访问密码
BASE_URL = "https://new-api.lkwplus.com"
OPENAI_API_KEY = "{your_openai_api_key}"
NEXT_TELEMETRY_DISABLE = "1"
NODE_VERSION = "20.1"
PHP_VERSION = "7.4"
YARN_VERSION = "1.22.19"

PS: Next.js 项目还是 Vercel 的支持更好,应该优先部署到 Vercel 而非 Cloudflare :)

excalidraw

用于绘制手绘风格的流程图,GitHub REAMDE,构建配置如下:

  • Build command: yarn --cwd ./excalidraw-app build
  • Build output: excalidraw-app/build

BroadcastChannel

用于将 Telegram Channel 转换为微博客,0 JS 的理念很有意思,文档地址,构建配置如下:

  • Build command: pnpm run build
  • Build output: dist

配置如下环境变量:

CHANNEL = "lkw123_channel"
GITHUB = "synthpop123"
LOCALE = "zh-cn"
TELEGRAM = "lkwtg"
TIMEZONE = "Asia/Shanghai"
TWITTER = "whoamamamiii"

IT-Tools

IT 工具箱,功能丰富、UI 美观,文档地址,构建配置如下:

  • Build command: pnpm run build
  • Build output: dist

Web Archive

Web Archive 可以提供类似于使用 Singlefile 插件进行网页存档的功能,服务基于 Cloudflare D1 数据库及 R2 存储桶。参考 官方文档 进行一键部署,从 Release 页下载 Chrome 插件后即可开始使用。

Quartz

将 Obsidian 的笔记内容构建为静态网页,可定制性强,是一个很好的官方 Obsidian Publish 功能的替代方案,文档地址。我仿照一个非常好看 Quartz 实例 Garden’s Gate 进行了一些修改。构建配置如下:

  • Build command: git fetch --unshallow && npx quartz build
  • Build output: public

Slidev

Slidev 是一个为开发者设计的基于 Web 的幻灯片制作工具,文档地址,构建配置如下:

  • Build command: pnpm run build
  • Build output: dist
  • Rewrites: "source": "/(.*)", "destination": "/index.html"

MkDocs

采用 Material for MkDocs 主题的文档站,GitHub Repo。构建配置如下:

  • Build command: mkdocs build
  • Build output: site

R2 Uploader

此前一直苦于没有美观好用的用于 Cloudflare R2 的 S3 兼容上传器,用如 PicGo 之类的软件会感觉太重,体验一般。

jw-12138/r2-uploader 则很好的规避了太重的问题,保留核心功能,还支持传输大文件、自动压缩、自定义域名等,且密钥等均通过浏览器存在本地,保障了安全的问题,部署教程详见 README

除此之外,轻量的命令行工具 pluveto/upgit 也很不错,非常推荐。

Sink

完全运行于 Cloudflare 的短链接生成工具,支持分析功能。部署教程详见 README

Dimension

此前的个人主页,稍稍修改自 HTML5 UP 设计的静态页面。

site-status

基于 UptimeRobot API 的在线状态面板,根据 README 中的部署教程,修改 .env 文件中的 UptimeRobot API Key 即可。

Clouflare Workers

gh-proxy

基于 Cloudflare Workers 对 GitHub Release、Archive 以及项目文件进行反代,从而实现加速访问,提供了一个简单的 Web UI,部署教程

DeepLX

详见项目 deeplx-for-cloudflare,可以给如沉浸式翻译插件或 Bob 等软件提供翻译 API 接口。

Reverse Proxy

基于 Cloudflare Workers 的简单反向代理脚本,且可以处理跨域请求,偶尔用于给无法访问源网站的朋友分享内容时会用到,部署教程详见 README

Pastebin

通过 Cloudflare KV 进行存储的 Pastebin 项目,用于临时分享代码或小文件等使用,部署教程详见 README

ChatGPT-Telegram-Workers

部署教程详见 README。环境变量如下:

API_KEY = "{your_api_key}"
CHAT_WHITE_LIST = "{your_tg_id}"
CUSTOM_COMMAND_35 = "/setenvs {'OPENAI_CHAT_MODEL': 'gpt-3.5-turbo'}"
CUSTOM_COMMAND_4o = "/setenvs {'OPENAI_CHAT_MODEL': 'gpt-4o'}"
CUSTOM_COMMAND_4o-mini = "/setenvs {'OPENAI_CHAT_MODEL': 'gpt-4o-mini'}"
CUSTOM_COMMAND_ds-coder = "/setenvs {'OPENAI_CHAT_MODEL': 'deepseek-coder'}"
CUSTOM_COMMAND_cn2en = "/setenvs {'SYSTEM_INIT_MESSAGE': '你是一个翻译,下面将我说的话都翻译成英文'}"
CUSTOM_COMMAND_en2cn = "/setenvs {'SYSTEM_INIT_MESSAGE': '你是一个翻译,下面将我说的话都翻译成中文'}"
DALL_E_IMAGE_QUALITY = "hd"
DALL_E_IMAGE_SIZE = "1024x1024"
DALL_E_IMAGE_STYLE = "natural"
DALL_E_MODEL = "dall-e-3"
ENABLE_USAGE_STATISTICS = "true"
GROUP_CHAT_BOT_ENABLE = "false"
OPENAI_API_BASE = "https://new-api.lkwplus.com/v1"
OPENAI_CHAT_MODEL = "gpt-4o"
TELEGRAM_AVAILABLE_TOKENS= "{your_bot_token}"

Oaifree Helper

提取多个 ChatGPT 账号的 AccessToken/RefreshToken 后,基于 Cloudflare Workers 反代始皇的 new.oaifree.com 镜像站,从而实现免梯访问 ChatGPT 和多号切换,部署教程详见 README

Fuclaude

依赖于始皇的 fuclaude,基于 Cloudflare Workers 实现号池分享,部署教程可参考 L 站帖子

Letterboxd Diary Embed

基于 Cloudflare Workers 获取任意用户的最新 Letterboxd 影评,并生成 HTML 代码以嵌入个人网站中,项目地址

其逻辑是发出 GET 请求后会首先在 Cloudflare KV 中查询是否有缓存,若有则直接返回缓存内容,若没有则向 Letterboxd API 发出请求获取最新影评,并将结果 HTML 代码缓存到 KV 中。

个人对缓存逻辑和生成的影评 CSS 样式进行了简单修改后重新部署,以添加 ?username=lkw123 参数为例发送请求,获取到的的内容如下:

<style>
  #letterboxd-embed-tc {
    margin-bottom: 1.5rem;
  }
 
  .letterboxd-embed-tc-diary-entry {
    margin-bottom: 1rem;
  }
 
  .letterboxd-embed-tc-content {
    display: flex;
    align-items: flex-start;
    gap: 2rem;
    margin-bottom: 1em;
    margin-top: 1em;
  }
 
  .letterboxd-embed-tc-poster img {
    margin: 0 !important;
    border-radius: 0.375rem;
    width: 70px;
    height: 105px;
    object-fit: cover;
  }
 
  .letterboxd-embed-tc-title {
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.25;
  }
 
  .letterboxd-embed-tc-year {
    font-size: 0.875rem;
    font-weight: 400;
    color: #6c757d;
    margin-left: 0.25rem;
  }
 
  .letterboxd-embed-tc-date {
    font-size: 0.875rem;
    color: #6c757d;
    margin-top: 0.25rem;
    margin-bottom: 0.25rem;
  }
 
  .letterboxd-embed-tc-rating {
    font-size: 1.125rem;
    font-weight: 500;
    color: #16a34a;
  }
 
  .letterboxd-embed-tc-review {
    font-size: 0.875rem;
    color: #6c757d;
    margin-top: 0.25rem;
  }
 
  .letterboxd-embed-tc-divider {
    border-bottom: 1px solid #6c757d;
    margin-top: 1rem;
    margin-bottom: 1rem;
  }
 
  .letterboxd-embed-tc-more {
    font-size: 1rem;
    color: #6c757d;
    transition: color 0.2s;
  }
 
  .letterboxd-embed-tc-more:hover {
    color: #000000;
  }
</style>
 
<div id="letterboxd-embed-tc">
  <div class="letterboxd-embed-tc-diary-entry">
    <div class="letterboxd-embed-tc-content">
      <div class="letterboxd-embed-tc-poster">
        <a
          href="https://letterboxd.com/lkw123/film/the-volunteers-the-battle-of-life-and-death/"
          target="_blank"
        >
          <img
            src="https://a.ltrbxd.com/resized/film-poster/1/1/8/0/9/3/3/1180933-the-volunteers-the-battle-of-life-and-death-0-70-0-105-crop.jpg?v=998f8665cf"
            alt="The Volunteers: The Battle of Life and Death poster"
          />
        </a>
      </div>
 
      <div>
        <div class="letterboxd-embed-tc-title">
          The Volunteers: The Battle of Life and Death
          <span class="letterboxd-embed-tc-year"> 2024 </span>
        </div>
 
        <div class="letterboxd-embed-tc-date">October 3, 2024</div>
 
        <div class="letterboxd-embed-tc-rating">★★½</div>
      </div>
    </div>
  </div>
 
  <div class="letterboxd-embed-tc-divider"></div>
 
  <div class="letterboxd-embed-tc-diary-entry">
    <div class="letterboxd-embed-tc-content">
      <div class="letterboxd-embed-tc-poster">
        <a
          href="https://letterboxd.com/lkw123/film/the-cloud-in-her-room/1/"
          target="_blank"
        >
          <img
            src="https://a.ltrbxd.com/resized/film-poster/5/0/4/5/1/8/504518-the-cloud-in-her-room-0-70-0-105-crop.jpg?v=60cbaf1d17"
            alt="The Cloud in Her Room poster"
          />
        </a>
      </div>
 
      <div>
        <div class="letterboxd-embed-tc-title">
          The Cloud in Her Room
          <span class="letterboxd-embed-tc-year"> 2020 </span>
        </div>
 
        <div class="letterboxd-embed-tc-date">&#9850; September 7, 2024</div>
 
        <div class="letterboxd-embed-tc-rating">★★★½</div>
 
        <div class="letterboxd-embed-tc-review">
          One of my favorite film posters
        </div>
      </div>
    </div>
  </div>
 
  <div class="letterboxd-embed-tc-divider"></div>
 
  <div class="letterboxd-embed-tc-diary-entry">
    <div class="letterboxd-embed-tc-content">
      <div class="letterboxd-embed-tc-poster">
        <a href="https://letterboxd.com/lkw123/film/alien/" target="_blank">
          <img
            src="https://a.ltrbxd.com/resized/sm/upload/8v/f1/qw/aa/bg7K6VtUG7Ew70gQj6SSroD5d4R-0-70-0-105-crop.jpg?v=a932f9e98e"
            alt="Alien poster"
          />
        </a>
      </div>
 
      <div>
        <div class="letterboxd-embed-tc-title">
          Alien
          <span class="letterboxd-embed-tc-year"> 1979 </span>
        </div>
 
        <div class="letterboxd-embed-tc-date">August 25, 2024</div>
 
        <div class="letterboxd-embed-tc-rating">★★★★</div>
      </div>
    </div>
  </div>
 
  <div class="letterboxd-embed-tc-divider"></div>
 
  <div class="letterboxd-embed-tc-diary-entry">
    <div class="letterboxd-embed-tc-content">
      <div class="letterboxd-embed-tc-poster">
        <a
          href="https://letterboxd.com/lkw123/film/alien-romulus/"
          target="_blank"
        >
          <img
            src="https://a.ltrbxd.com/resized/film-poster/8/5/0/4/5/9/850459-alien-romulus-0-70-0-105-crop.jpg?v=acabb7fd83"
            alt="Alien: Romulus poster"
          />
        </a>
      </div>
 
      <div>
        <div class="letterboxd-embed-tc-title">
          Alien: Romulus
          <span class="letterboxd-embed-tc-year"> 2024 </span>
        </div>
 
        <div class="letterboxd-embed-tc-date">August 21, 2024</div>
 
        <div class="letterboxd-embed-tc-rating">★★★★</div>
      </div>
    </div>
  </div>
 
  <div class="letterboxd-embed-tc-divider"></div>
 
  <div class="letterboxd-embed-tc-diary-entry">
    <div class="letterboxd-embed-tc-content">
      <div class="letterboxd-embed-tc-poster">
        <a
          href="https://letterboxd.com/lkw123/film/despicable-me-4/"
          target="_blank"
        >
          <img
            src="https://a.ltrbxd.com/resized/film-poster/4/4/8/5/0/6/448506-despicable-me-4-0-70-0-105-crop.jpg?v=3391582e43"
            alt="Despicable Me 4 poster"
          />
        </a>
      </div>
 
      <div>
        <div class="letterboxd-embed-tc-title">
          Despicable Me 4
          <span class="letterboxd-embed-tc-year"> 2024 </span>
        </div>
 
        <div class="letterboxd-embed-tc-date">August 13, 2024</div>
 
        <div class="letterboxd-embed-tc-rating">★★</div>
      </div>
    </div>
  </div>
 
  <div class="letterboxd-embed-tc-divider"></div>
 
  <div class="letterboxd-embed-tc-more">
    <a href="https://letterboxd.com/lkw123" target="_blank"
      >...more on Letterboxd</a
    >
  </div>
</div>

可以通过如下方式将其嵌入到博客中。具体效果详见我的博客的 Movies 页面

<div id="letterboxd-embed-wrapper-tc">Loading...</div>
 
<script>
  fetch('https://letterboxd-embed.lkwplus.com?username=lkw123')
    .then((response) => response.text())
    .then((data) => {
      const element = document.getElementById('letterboxd-embed-wrapper-tc')
      if (element) {
        element.innerHTML = data
      }
    })
</script>

PT Gen

根据豆瓣、IMDb、Bangumi、Steam 链接自动生成简介,主要用于 PT(自动化)发种,文档地址

Vercel

此前在 Vercel 上部署的项目很多,但自决定全面转向 Cloudflare 的 CDN 和各类服务后,逐步对项目进行迁移。

RSSHub

文档地址

RSSHub 是一个开源的、用于将各种网站的内容聚合到一个统一的接口中生成 RSS Feed 的项目,主要用于订阅未提供 RSS Feed 的网站或社交媒体。在这个碎片化信息爆炸的时代,做一些试图逃脱信息茧房的挣扎。

根据 GitHub Issue #14622 的讨论,在今年上半年起 Vercel/Cloudflare Workers 无法部署 RSSHub 的最新代码,测试可用的最新 Commit 截至 29276d8。这个问题的修复目前卡在 got 和 NextJS 不兼容,需要等待 got 依赖从 RSSHub 项目移除。

暂时性的解决方案为:首先将 DIYgod/RSSHub 完整的 Fork 至自己账号下,可以在 Vercel 的 Create Deployment 处填入相应的 Commit 链接 https://github.com/{your-username}/RSSHub/commit/29276d8,或是在 GitHub 将默认分支由 master 切换至 legacy 分支后再部署。

Koodo Reader

跨平台的电子书阅读器,部署教程详见 README

Umami

Google Analytics 的开源替代,项目地址

采用 Vercel 免费提供的 Postgres 作为后端数据库,和项目进行关联后,在部署页面配置数据库连接信息即可。同时,可以修改 TRACKER_SCRIPT_NAME 环境变量为不包含 umami、analytics 等关键字的名称,避免目标网站中引入的 js 脚本被 Adblock 等插件拦截。

Nobelium

项目地址,基于 Notion 的 Next.js 静态博客,类似的开源解决方案如 NotionNext,闭源解决方案如 Super,或是直接选择使用官方的 Notion Website 功能即可。

Homer

项目地址,极简风格的静态面板,比较适合用于服务器、Homelab、NAS 等,可以方便的集中整理自己的各类自部署服务或书签。

DailyHot

项目后端 imsyy/DailyHotApi-Vercel,项目前端 imsyy/DailyHot。后端部署完毕后,更改前端项目的环境变量 VITE_GLOBAL_API 即可,如 “https://dailyhot-api.lkwplus.com”。

Last.fm Recently Played

项目地址,用于生成个人的最新 Last.fm 听歌记录,可美观的展示于 GitHub Profile 或其他地方,类似的还可以选择同开发者的 JeffreyCA/spotify-recently-played-readme 项目。

由于该仓库自身提供服务使用的 *.vercel.app 子域名被墙,因此部署后绑定自定义域名以规避这个问题。具体效果详见我的博客的 Music 页面

NetEase Music API

网易云音乐 Node.js API service,原仓库由于版权问题现已删库,如有需求可以考虑部署 PHP 实现 kilingzhang/NeteaseCloudMusicApi。自己此前 fork 的 仓库 尚且幸存,部署的 API 一直可以能正常使用。此前主要用于配合 qier222/YesPlayMusic 在 Vercel 搭建在线音乐 Web 端,但很少使用。

fly.io

我的 fly.io 账户幸运的停留在了 Legacy Hobby plan,可以免费部署三个 cpu-1x mem-256M 的实例,相当的大方且慷慨,感激 :)

Reader

阅读的服务器端项目,可以方便的在网页端摸鱼看小说。文档地址fly.toml 文件内容如下:

fly.toml
app = 'lkw123-reader'
primary_region = 'iad'
 
[build]
  image = "hectorqin/reader:openj9-latest"
 
[env]
  SPRING_PROFILES_ACTIVE = "prod"
  READER_APP_USERLIMIT = "1"
  READER_APP_USERBOOKLIMIT = "200"
  READER_APP_CACHECHAPTERCONTENT = "true"
  READER_APP_SECURE = "true"
  READER_APP_SECUREKEY = "{your_secret_key}"
 
[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = 'off'
  auto_start_machines = false
  min_machines_running = 0
  processes = ['app']
 
[mounts]
  source="reader_data"
  destination="/storage"
 
[[vm]]
  size = 'shared-cpu-1x'

FastAPI

源码存放于我的 GitHub 仓库 synthpop123/fly-fastapifly.toml 文件内容如下:

fly.toml
app = 'lkw123-fastapi'
primary_region = 'iad'
 
[build]
  builder = 'paketobuildpacks/builder:base'
 
[env]
  PORT = '8000'
 
[http_service]
  internal_port = 8000
  force_https = true
  auto_stop_machines = 'off'
  auto_start_machines = false
  min_machines_running = 0
  processes = ['app']
 
[[vm]]
  size = 'shared-cpu-1x'

Vaultwarden

使用 Rust 构建的开源密码管理器 Bitwarden 的服务端实现,且和上游的 Bitwarden 客户端兼容,fly.toml 文件内容如下:

fly.toml
app = 'lkw123-vault'
primary_region = 'iad'
 
[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = 'off'
  auto_start_machines = false
  min_machines_running = 0
  processes = ['app']
 
[env]
  SIGNUPS_ALLOWED = "false"
 
[build]
  image = "vaultwarden/server:latest"
 
[mounts]
  source = "vw_data"
  destination = "/data"
 
[[services]]
  http_checks = []
  internal_port = 80
  [[services.ports]]
    force_https = true
    handlers = ["http"]
    port = 80
 
  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443
 
  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"
 
[[vm]]
  size = 'shared-cpu-1x'

Memos

参考 hu3rror/memos-on-fly 的文档进行部署,除了保有项目的原功能外,还支持通过 Litestream 自动备份数据库到个人的 Backblaze B2

为了接入 Telegram Bot,需要采用标签为 stable-memogram 的镜像,并需要手动添加 Bot Token 至环境变量:

flyctl secrets set BOT_TOKEN="{your_bot_token}"

fly.toml 文件内容如下:

fly.toml
app = 'lkw123-memos'
primary_region = 'iad'
 
[build]
  image = 'ghcr.io/hu3rror/memos-litestream:stable-memogram'
 
[env]
  LITESTREAM_REPLICA_BUCKET = '{lkw123-memos}'
  LITESTREAM_REPLICA_ENDPOINT = '{s3.us-west-004.backblazeb2.com}'
  LITESTREAM_REPLICA_PATH = 'memos_prod.db'
 
[[mounts]]
  source = 'memos_data'
  destination = '/var/opt/memos'
 
[http_service]
  internal_port = 5230
  force_https = true
  auto_stop_machines = false
  auto_start_machines = true
  min_machines_running = 0
 
[[vm]]
  size = 'shared-cpu-1x'

顺手记录一下自己的 Memos 自定义 CSS 样式:

custom.css
/* 设置 Memos 标签样式 */
span.inline-block.w-auto.text-blue-600.dark\:text-blue-400 {
  color: #f3f3f3;
  background-color: #40b76b;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  border-radius: 2px;
  padding: 2px 6px;
  font-size: 15px;
  margin-bottom: 4px;
}
/* 设置同级下不同的标签显示不同的颜色 */
/* 第2个标签 */
span.inline-block.w-auto.text-blue-600.dark\:text-blue-400:nth-child(n + 2) {
  background-color: #157cf5;
}
/* 第3个标签 */
span.inline-block.w-auto.text-blue-600.dark\:text-blue-400:nth-child(n + 4) {
  background-color: #f298a6;
}
/* 第4个标签 */
span.inline-block.w-auto.text-blue-600.dark\:text-blue-400:nth-child(n + 6) {
  background-color: #fdb15d;
}
/* 第5个标签 */
span.inline-block.w-auto.text-blue-600.dark\:text-blue-400:nth-child(n + 8) {
  background-color: #67d6ca;
}
/* 第6个标签 */
span.inline-block.w-auto.text-blue-600.dark\:text-blue-400:nth-child(n + 10) {
  background-color: #7445e0;
}

Zeabur

Zeabur 是一个比较新的云端部署服务,作为国人团队开发的产品,它会有更多的本地化支持和对大陆用户的优化。

在通常情况下,我们使用如 Zeabur 这类 PaaS 服务时,主要是将项目部署至其多区域的共享集群 (Shared Cluster),利用其 Edge 节点提供的高速网络连接,同时降低运维成本。

Zeabur 最近推出了“独立服务器”的功能,用户可以将自己的服务器注册到 Zeabur 上,从而无需支付硬件资源的使用费用,又能享受到平台为部署流程带来的便利。我用 VKVM 的洛杉矶 9950X 4C8G VPS 测试了下,效果还不错,但是占用系统资源比较多,如果有闲置的大内存机器可以尝试。

Blog

基于 Astro 构建,修改自 astro-erudite,并使用了 astro-theme-pure 中的部分组件样式。

Homepage

个人很喜欢博客主题 astro-erudite 的样式,就根据博客 clone 了一份,去除无用的依赖,保留 index.astro 单页面中的 Card 组建,并稍微调整样式,便成了新的个人主页。

Blog Legacy

基于 Astro 构建,修改自 AstroPaper,部分修改过程记录在了博文 AstroPaper 博客自定义 中,代码存放于我的 GitHub 仓库 synthpop123/astro-blog。自迁移至新的主题后,该旧博客暂时保留。


参考:

Comments