博客配置
1c-1g-30G/postgres+halo
搭建步骤
配置环境
截止博客更新时, Halo 要求 JRE 版本 ≥ 17,前往官网 下载最新版本
- 下载java
wget -qO- https://download.java.net/java/GA/jdk23/3c5b90190c68498b986a97f276efd28a/37/GPL/openjdk-23_linux-x64_bin.tar.gz | sudo tar -xz -C /opt/
- 更新环境变量
sudo cat <<EOF >> /etc/profile export JAVA_HOME=/opt/jdk-23 export PATH=\$PATH:\$JAVA_HOME/bin EOF
-
更新系统默认Java版本
sudo update-alternatives --install /usr/bin/java java /opt/jdk-23/bin/java 1 sudo update-alternatives --install /usr/bin/javac javac /opt/jdk-23/bin/javac 1
-
检查是否配置正确
sudo update-alternatives --config java sudo update-alternatives --config javac java -version
配置数据库
-
安装postgres
sudo apt update sudo apt install postgresql postgresql-contrib
- 优化配置文件
sudo vi /etc/postgresql/{version}/main/postgresql.conf
修改以下配置项
shared_buffers = 32MB work_mem = 1MB maintenance_work_mem = 16MB autovacuum = on
-
创建数据库
sudo -u postgres psql
CREATE DATABASE halo; CREATE USER halo_user WITH PASSWORD 'secure_password'; ALTER ROLE halo_user SET client_encoding TO 'utf8'; ALTER ROLE halo_user SET default_transaction_isolation TO 'read committed'; ALTER ROLE halo_user SET timezone TO 'UTC'; GRANT ALL PRIVILEGES ON DATABASE halo TO halo_user; \q
配置halo
-
创建用户目录
useradd -m halo passwd halo su - halo
-
下载halo.jar
在 ~/app 目录下载halo 最新版本的运行包,截止博客更新时,最新版本 v2.20.11,点击前往 发布页
mkdir ~/app && curl -s https://api.github.com/repos/halo-dev/halo/releases/latest | grep "browser_download_url" | grep "\.jar\"" | cut -d '"' -f 4 | xargs curl -L -o ~/app/halo.jar
-
创建配置文件
mkdir ~/.halo2 && cd ~/.halo2 vim application.yaml server: # 运行端口 port: 8080 spring: r2dbc: url: r2dbc:pool:postgresql://localhost:5432/halo username: halo password: biubiubiu sql: init: mode: always platform: postgresql halo: caches: page: # 是否禁用页面缓存 disabled: false # 工作目录位置 work-dir: ${user.home}/.halo2 # 外部访问地址 external-url: http://localhost:8080 # 附件映射配置,通常用于迁移场景 attachment: resource-mappings: - pathPattern: /upload/** locations: - migrate-from-1.x
-
测试运行
cd ~/app && java -Dfile.encoding=UTF-8 -jar halo.jar --spring.config.additional-location=optional:file:$HOME/.halo2/
打开 http://ip:8080 即可跳转到初始化页面
配置系统服务
-
切换回root用户创建 halo.service
exit vim /etc/systemd/system/halo.service [Unit] Description=Halo Service Documentation=https://docs.halo.run After=network-online.target Wants=network-online.target [Service] Type=simple User=halo ExecStart=/usr/bin/java -Dfile.encoding=UTF-8 -server -Xms128m -Xmx192m -Xss256k -XX:+UseSerialGC -jar /home/halo/app/halo.jar --spring.config.additional-location=optional:file:/home/halo/.halo2/ ExecStop=/bin/kill -s QUIT $MAINPID Restart=always RestartSec=60 RuntimeMaxSec=259200 OOMScoreAdjust=500 StandardOutput=journal StandardError=inherit [Install] WantedBy=multi-user.target
-
运行服务
sudo systemctl daemon-reload sudo systemctl start halo sudo systemctl enable halo
-
查看日志
journalctl -n 20 -u halo
配置nginx
-
下载证书
前往 ZeroSSL 下载90天证书并将证书解压到 /etc/nginx/ssl/domain.com
-
替换 /etc/nginx/nginx.conf
user www-data; worker_processes 1; pid /run/nginx.pid; worker_rlimit_nofile 4096; events { worker_connections 1024; use epoll; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 30; types_hash_max_size 1024; client_max_body_size 128m; server_tokens off; include /etc/nginx/mime.types; default_type application/octet-stream; access_log off; error_log /var/log/nginx/error.log; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:5m; ssl_session_timeout 10m; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; open_file_cache max=500 inactive=10s; open_file_cache_valid 20s; open_file_cache_min_uses 1; open_file_cache_errors on; server { listen 80; server_name domain.com; return 301 https://$host$request_uri; } server { listen 443 ssl; http2 on; server_name domain.com; ssl_certificate /etc/nginx/ssl/domain.com/certificate.crt; ssl_certificate_key /etc/nginx/ssl/domain.com/private.key; ssl_trusted_certificate /etc/nginx/ssl/domain.com/ca_bundle.crt; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; location / { proxy_pass http://localhost:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering on; proxy_buffer_size 64k; proxy_buffers 4 128k; proxy_busy_buffers_size 128k; proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; } } }
备份和恢复
进入后台,点击左侧 备份 - + 创建备份,备份后依次点击右侧 ··· - 下载 下载到本地
进入后台,点击左侧 备份 - 恢复 - 我已阅读上方提示,开始恢复,选择备份文件进行恢复
更新
为避免破坏性更新,建议等待新版本发布一周后再考虑更新,更新前先停止服务
-
停止服务
sudo systemctl stop halo
-
下载jar包
切换到 halo 用户,在 ~/app 目录下载最新版本的 Halo 运行包,覆盖原有的运行包
su halo curl -s https://api.github.com/repos/halo-dev/halo/releases/latest | grep "browser_download_url" | grep "\.jar\"" | cut -d '"' -f 4 | xargs curl -L -o ~/app/halo.jar
-
重启服务
切换回管理员用户执行重启
exit sudo systemctl start halo
番外一:配置Umami服务
Umami 是一款开源的类 Google Analytics 工具,Halo 已经提供了插件支持,可直接在插件市场搜索安装插件搭配 Umami 服务使用
容器部署
使用 render、koyeb这样的免费容器部署,克隆仓库:https://github.com/Raimbaulty/umami 后连接到仓库
环境变量
变量名称 | 示例值 | 描述 |
---|---|---|
ALLOWED_FRAME_URLS | https://example.com,https://domain.com |
允许嵌入的站点地址,多个用逗号分隔 |
APP_SECRET | your_app_secret_key |
应用安全密钥,务必保密 |
DATABASE_TYPE | postgres |
数据库类型,如 mysql , postgres , sqlite 等 |
DATABASE_URL | postgresql://user:password@host:port/db |
数据库连接 URL |
TRACKER_SCRIPT_NAME | index.js |
自命名追踪脚本,防止被广告拦截插件拦截 |
添加站点
服务运行后,访问容器地址使用默认用户名 admin
默认密码 umami
登录,点击右上角 🌐 切换语言为中文
导航栏点击 设置 → + 添加网站,填写网站名称和域名(不带 https://
协议头)后 保存
点击 编辑,首先复制 网站 ID 保存,然后切换到 共享链接 标签,打开 启用共享链接 并复制 Your website stats are publicly available at the following URL:
的值作为下一步 共享链接 的值填入
配置插件
在应用市场搜索 Umami 并安装,然后在 插件 开启 Umami 并点击 ··· 进入 详情,切换到 设置 标签进行设置
- Umami 站点地址 填写容器地址
- 脚本名称 填写与
TRACKER_SCRIPT_NAME
保持一致 - 站点 ID 填写上一步复制的 网站 ID
- 共享链接 填写上一步得到的值
传统部署
配置环境
su - halo
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
source ~/.profile
nvm install 18
nvm use 18
npm install -g yarn
配置数据库
sudo -u postgres psql
CREATE DATABASE umami;
CREATE USER umami_user WITH PASSWORD 'secure_password';
ALTER ROLE umami_user SET client_encoding TO 'utf8';
ALTER ROLE umami_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE umami_user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE umami TO umami_user;
\q
配置umami
su - halo
git clone https://github.com/umami-software/umami.git umami && cd $_
cat > .env <<EOF
DATABASE_URL=postgresql://umami_user:secure_password@localhost:5432/umami
EOF
yarn install
yarn build
如果是1C1G的轻量机,先终止其他服务释放内存然后执行下面的指令构建
NODE_OPTIONS="--max-old-space-size=1024" yarn build
配置服务
sudo cat <<EOF > /etc/systemd/system/umami.service
[Unit]
Description=Umami Service
After=network.target
[Service]
Type=simple
User=halo
ExecStart=/home/halo/.nvm/versions/node/v18.20.7/bin/yarn start
Restart=always
Environment=PATH=/usr/bin:/usr/local/bin:/home/halo/.nvm/versions/node/v18.20.7/bin
Environment=NODE_ENV=production
WorkingDirectory=/home/halo/umami
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl start umami
sudo systemctl enable umami
sudo systemctl status umami
配置nginx
注意替换umami.com
为umami站点地址,domain.com
为halo站点地址
server {
listen 80;
listen [::]:80;
server_name umami.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name umami.com;
ssl on;
ssl_certificate /etc/nginx/cert/umami.com/certificate.crt;
ssl_certificate_key /etc/nginx/cert/umami.com/private.key;
ssl_trusted_certificate /etc/nginx/ssl/umami.com/ca_bundle.crt;
add_header Access-Control-Allow-Origin 'https://[halo.domain.com]';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
add_header Content-Security-Policy 'frame-ancestors domain.com';
location / {
proxy_pass http://localhost:3000;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_hide_header 'Content-Security-Policy';
}
}
更新
sudo systemctl stop umami
su - halo && cd umami
git pull
yarn install
yarn build
sudo systemctl start umami
番外二:部署Huggingface备份
以下教程使用 H2 数据库,只建议用作博客备份
创建空间
点击这里 创建,space name 填写 halo,License 选择 Apache2.0,点击 Docker - Blank,确认勾选 Public,然后点击 Create Space 创建
空间创建后,需要修改或创建以下文件,.gitattributes
无需修改
📦 main
├── 📄 .gitattributes
├── 📄 Dockerfile
├── 📄 sync_data.sh
└── 📄 README.md
下面依次介绍除 .gitattributes
外各文件配置
配置Dockerfile
-
点击 Files 菜单 - + Add file - Create a new file
-
Name your file 填写
Dockerfile
,并将以下代码复制到 Edit 下方后点击 Commit changes tomain
提交FROM eclipse-temurin:23-jre WORKDIR /opt/halo ENV TZ=Asia/Shanghai ENV JVM_OPTS="-Xmx256m -Xms256m" RUN apt-get update && \ apt-get install -y curl python3 python3-venv python3-pip tar zip && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* RUN curl -s https://api.github.com/repos/halo-dev/halo/releases/latest | grep "browser_download_url" | grep "\.jar\"" | cut -d '"' -f 4 | xargs curl -L -o halo.jar RUN mkdir -p ~/.halo2 ENV VIRTUAL_ENV=/opt/venv RUN python3 -m venv $VIRTUAL_ENV ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN pip install --no-cache-dir huggingface_hub COPY sync_data.sh /opt/halo/ RUN chmod +x /opt/halo/sync_data.sh EXPOSE 8090 CMD ["bash", "-c", "exec /opt/halo/sync_data.sh"]
配置sync_data.sh
-
操作同上,Name your file 填写
sync_data.sh
,复制下方代码后点击 Commit changes tomain
提交#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -a log() { echo "[$(date +'%F %T')] $*" } init_backup(){ if [[ -n "${DATASET_ID:-}" ]]; then log "📁 使用外部定义的 DATASET_ID=$DATASET_ID" return 0 fi if [[ -z "${HF_TOKEN:-}" ]]; then log "⚠️ HF_TOKEN 未设置,跳过备份" return 1 fi USER_ID=$(python3 - <<'PY' import os,sys from huggingface_hub import HfApi try: name = HfApi(token=os.getenv("HF_TOKEN")).whoami().get("name","") print(name) if name else sys.exit(1) except: sys.exit(1) PY ) if [[ -z "$USER_ID" ]]; then log "⚠️ 获取 USER_ID 失败,跳过备份" return 1 fi DATASET_ID="${USER_ID}/data" # ← 这里修正了变量名 log "✅ 设置默认 DATASET_ID=$DATASET_ID" return 0 } prep_repo(){ python3 <<'PY' import os from huggingface_hub import HfApi api = HfApi(token=os.getenv("HF_TOKEN")) repo = os.environ["DATASET_ID"] author = repo.split("/")[0] if not any(d.id == repo for d in api.list_datasets(author=author)): api.create_repo(repo_id=repo, repo_type="dataset", private=True) branch = "main" refs = api.list_repo_refs(repo_id=repo, repo_type="dataset").branches if branch not in [b.name for b in refs]: api.create_branch(repo_id=repo, repo_type="dataset", branch=branch) PY log "✅ 数据集 & 分支就绪" } restore_latest(){ python3 <<'PY' import os,sys,tarfile,tempfile from huggingface_hub import HfApi api = HfApi(token=os.getenv("HF_TOKEN")) repo, branch = os.getenv("DATASET_ID"), "main" files = api.list_repo_files(repo_id=repo, repo_type="dataset", revision=branch) backs = sorted(f for f in files if f.endswith(".tar.gz")) if not backs: sys.exit(0) td = tempfile.mkdtemp() path = api.hf_hub_download(repo_id=repo, repo_type="dataset", revision=branch, filename=backs[-1], local_dir=td) with tarfile.open(path) as t: t.extractall(os.getenv("BACKUP_DIR")) PY log "✅ 恢复最新备份(如果有)" } do_backup(){ ts=$(date +%Y%m%d_%H%M%S) fname="Backup_${ts}.tar.gz" tmp=$(mktemp -d) tar -czf "$tmp/$fname" -C "$BACKUP_DIR" . python3 <<PY import os from huggingface_hub import HfApi api = HfApi(token=os.getenv("HF_TOKEN")) repo, branch = os.getenv("DATASET_ID"), "main" api.upload_file(path_or_fileobj="$tmp/$fname", path_in_repo="$fname", repo_id=repo, repo_type="dataset", revision=branch) keep = int(os.getenv("DATASET_NUM", "10")) files = api.list_repo_files(repo_id=repo, repo_type="dataset", revision=branch) backs = sorted(f for f in files if f.endswith(".tar.gz")) for old in backs[:-keep]: api.delete_file(path_in_repo=old, repo_id=repo, repo_type="dataset", revision=branch) api.super_squash_history(repo_id=repo, repo_type="dataset", branch=branch) PY rm -rf "$tmp" log "✅ 上传备份并清理临时文件" } sync_loop(){ while true; do do_backup log "⏳ 下次同步在 ${SYNC_INTERVAL}s 后" sleep "${SYNC_INTERVAL}" done } main(){ BACKUP_DIR="${BACKUP_DIR:-$HOME/.halo2}" DATASET_NUM="${DATASET_NUM:-10}" SYNC_INTERVAL="${SYNC_INTERVAL:-36000}" if init_backup; then log "🚀 启动备份/同步流程,使用数据集:$DATASET_ID" prep_repo restore_latest sync_loop & # 后台 else log "🚀 直接启动主应用,无备份/同步" fi exec java ${JVM_OPTS} -jar /opt/halo/halo.jar } main
配置README.md
-
点击 Files 菜单,点击 README.md 进入文件详情
-
点击 edit 编辑文件,添加 app_aport 后点击 Commit changes to
main
提交--- title: Halo emoji: 🌍 colorFrom: pink colorTo: red sdk: docker pinned: false license: apache-2.0 app_port: 8090 ---
添加Secrets
点击 Settings 菜单,下滑找到并点击New secret
按钮
- 必填项
变量名 | 示例值 | 描述 |
---|---|---|
HF_TOKEN | hf_xxxxxxxxxxxxxx | Huggingface令牌:前往创建 |
- 可填项,默认值如下,一部无需自定义
变量名 | 示例值 | 描述 |
---|---|---|
DATASET_ID | Owner/data | 数据集地址 |
SYNC_INTERVAL | 36000 | 备份间隔,单位:秒 |
DATASET_N | 10 | 备份容量 |
BACKUP_DIR | $HOME/.halo2 | 备份目录 |
备份到Huggingface
-
访问隧道绑定的域名初始化站点并登录后台
-
登入后点击左侧 备份 菜单 - 恢复 - 我已阅读上方提示,开始恢复
-
点击 浏览 选择备份,然后点击 上传1个文件,并点击 确定
-
等待重启,重启完成页面会自动刷新,接着输入正确的用户名和密码即可登录后台,点击 Halo 即可访问博客页面