Ubuntu 22.04 安装 VoucherVault 指南
在 Ubuntu 22.04 服务器上从源码安装 VoucherVault v1.30.3 的完整实录:系统依赖、venv、.env、数据库迁移、Celery/uWSGI 启动、Nginx 反代、systemd 托管与排障。
Ubuntu 22.04 安装 VoucherVault 指南
目标:在一台 Ubuntu 22.04 服务器上,从源码完整安装 VoucherVault(Django + uWSGI + Celery + Redis 架构),完成数据库初始化、管理员创建、服务启动、Nginx 反向代理配置与功能验证。全部命令与回显均来自 2026-10-25 的真实安装实录。VoucherVault 是什么、是否适合你的场景,请先读《VoucherVault 介绍与选型指南》。
一、环境信息
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS (Jammy Jellyfish),x86_64 |
| 软件版本 | VoucherVault v1.30.3(tag,提交 00fb937,2026-10-09 发布) |
| Python | 3.10.12(系统自带) |
| 关键依赖 | Django 5.2.18 / uWSGI 2.0.31 / Celery 5.6.3 / Apprise 1.9.9 / psycopg2-binary |
| Redis | 6.0.16(Ubuntu 仓库版) |
| Nginx | 1.18.0(Ubuntu 仓库版) |
| 安装路径 | /opt/vouchervault/(源码 app/、虚拟环境 venv/、日志 logs/) |
| 运行用户 | www-data(UID 33,与官方运行时保持一致) |
| 数据库 | SQLite3(默认,数据文件 /opt/vouchervault/app/database/db.sqlite3) |
💡 说明:VoucherVault 官方只维护一体化交付物,裸机安装需要自行拼装 Python 依赖与进程托管,本篇把这条路径完整走通。需要 PostgreSQL 作为后端库时,差异点见文末「进阶」一节。
二、安装系统依赖
VoucherVault 是 Python/Django 应用,运行前需要 Python 3、构建工具(编译 uWSGI、psycopg2 等)、Ghostscript(PDF 处理)、Redis(Celery 消息队列)与 Nginx(反向代理)。以 root 身份执行:
apt-get update
apt-get install -y python3 python3-pip python3-venv python3-dev git curl \
redis-server nginx-light ghostscript gcc libc6-dev libpq-dev \
netcat-openbsd postgresql-client unzip media-types
验证关键组件版本:
python3 --version
Python 3.10.12
redis-server --version
Redis server v=6.0.16 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=d6ed09173b889769
nginx -v
nginx version: nginx/1.18.0 (Ubuntu)
三、获取源码
把官方仓库克隆到 /opt/vouchervault/app 并切换到最新稳定版 v1.30.3:
mkdir -p /opt/vouchervault
git clone --depth 1 --branch v1.30.3 https://github.com/l4rm4nd/VoucherVault.git /opt/vouchervault/app
Cloning into '/opt/vouchervault/app'...
...
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
确认版本:
cd /opt/vouchervault/app && git describe --tags
v1.30.3
四、创建虚拟环境并安装依赖
官方交付物在构建期用 pip 安装 requirements.txt,裸机用 venv 隔离:
python3 -m venv /opt/vouchervault/venv
source /opt/vouchervault/venv/bin/activate
pip install --upgrade pip
pip install -r /opt/vouchervault/app/requirements.txt
依赖较多(Django、uWSGI、Celery、Apprise、psycopg2-binary 等),编译 uWSGI 需要一两分钟。装完抽查关键包:
pip list | grep -iE "^(django|uwsgi|celery|redis|apprise|django-celery-beat|mozilla-django-oidc|django-pwa|psycopg2)"
apprise 1.9.9
celery 5.6.3
Django 5.2.18
django-celery-beat 2.9.0
django-pwa 2.0.1
mozilla-django-oidc 5.0.2
psycopg2-binary 2.9.11
redis 6.4.0
uWSGI 2.0.31
五、初始化配置(.env)
VoucherVault 通过项目根目录的 .env 文件读取配置(settings.py 用 python-dotenv 自动加载)。创建 /opt/vouchervault/app/.env:
cat > /opt/vouchervault/app/.env <<'EOF'
DOMAIN=localhost
SECURE_COOKIES=False
SESSION_COOKIE_AGE=30
SESSION_EXPIRE_AT_BROWSER_CLOSE=False
EXPIRY_THRESHOLD_DAYS=30
TZ=Asia/Shanghai
REDIS_URL=redis://127.0.0.1:6379/0
EOF
关键项说明:
DOMAIN:对外访问域名或 IP,决定ALLOWED_HOSTS与CSRF_TRUSTED_ORIGINS,必须与实际访问地址一致,否则登录提交会被 CSRF 拦截;REDIS_URL:Redis 连接串,Celery 任务队列依赖它;EXPIRY_THRESHOLD_DAYS:到期前多少天首次提醒;SECRET_KEY不显式设置时每次启动随机生成(会导致既有会话失效),生产环境建议生成一个固定值写入.env。
全部环境变量的逐项说明见本专栏《VoucherVault 配置参数详解》。
六、修正目录属主
应用以低权限 www-data(UID 33)运行,数据库文件、静态文件、日志目录都要可写:
mkdir -p /opt/vouchervault/app/database /opt/vouchervault/logs
chown -R www-data:www-data /opt/vouchervault
七、初始化数据库与静态文件
以下命令均以 www-data 身份在 /opt/vouchervault/app 下执行(用 runuser -u www-data -- 切换):
cd /opt/vouchervault/app
runuser -u www-data -- /opt/vouchervault/venv/bin/python manage.py makemigrations
runuser -u www-data -- /opt/vouchervault/venv/bin/python manage.py makemigrations myapp
No changes detected
No changes detected in app 'myapp'
runuser -u www-data -- /opt/vouchervault/venv/bin/python manage.py migrate
首次执行会应用全部迁移,尾部输出如下:
Applying myapp.0027_add_view_mode_and_is_pinned... OK
Applying myapp.0028_alter_userpreference_show_description_and_more... OK
Applying myapp.0029_add_currency_to_item_and_userpreference... OK
Applying myapp.0030_transaction_date_editable... OK
Applying sessions.0001_initial... OK
收集静态文件(STATIC_ROOT 为 myapp/static/,稍后由 uWSGI/Nginx 提供):
runuser -u www-data -- /opt/vouchervault/venv/bin/python manage.py collectstatic --no-input --verbosity=0
ls /opt/vouchervault/app/myapp/static
admin
assets
css
fonts
images
八、创建定时任务与管理员
VoucherVault 的到期提醒依赖两条 Celery 周期任务,官方启动脚本在首次启动时通过自定义命令 create_default_periodic_tasks 创建:
runuser -u www-data -- /opt/vouchervault/venv/bin/python manage.py create_default_periodic_tasks
Default periodic tasks created successfully.
创建管理员账号(示例账号 admin,密码 q1w2e3r4;官方流程是随机生成密码打印到日志,裸机手动设置更可控):
runuser -u www-data -- /opt/vouchervault/venv/bin/python manage.py shell -c "from django.contrib.auth.models import User; User.objects.create_superuser('admin','admin@example.com','q1w2e3r4')"
18 objects imported automatically (use -v 2 for details).
九、启动服务
VoucherVault 裸机运行需要 4 个进程:Redis、Celery Worker、Celery Beat、uWSGI。
9.1 启动 Redis
redis-server --daemonize yes --port 6379
redis-cli ping
PONG
9.2 启动 Celery Worker 与 Beat
cd /opt/vouchervault/app
runuser -u www-data -- /opt/vouchervault/venv/bin/celery -A myproject worker -l info --detach \
--logfile=/opt/vouchervault/logs/celery-worker.log \
--pidfile=/opt/vouchervault/logs/celery-worker.pid
runuser -u www-data -- /opt/vouchervault/venv/bin/celery -A myproject beat -l info --detach \
--scheduler django_celery_beat.schedulers:DatabaseScheduler \
--logfile=/opt/vouchervault/logs/celery-beat.log \
--pidfile=/opt/vouchervault/logs/celery-beat.pid
检查日志确认连上 Redis:
tail -5 /opt/vouchervault/logs/celery-worker.log
[2026-10-25 01:40:18,299: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0
[2026-10-25 01:40:18,300: INFO/MainProcess] mingle: searching for neighbors
[2026-10-25 01:40:19,306: INFO/MainProcess] mingle: all alone
[2026-10-25 01:40:19,313: INFO/MainProcess] celery@vouchervault-server ready.
⚠️ Beat 必须带
--scheduler django_celery_beat.schedulers:DatabaseScheduler,否则不会读取数据库里刚创建的周期任务,到期提醒不会触发。
9.3 启动 uWSGI
官方交付物内的 docker_uwsgi.ini 写的是构建期路径 /opt/app,裸机按实际路径改写为 /opt/vouchervault/uwsgi.ini:
cat > /opt/vouchervault/uwsgi.ini <<'EOF'
[uwsgi]
http-socket = :8000
chdir = /opt/vouchervault/app
module = myproject.wsgi
master = 1
processes = 2
threads = 2
uid = www-data
gid = www-data
static-map = /static=/opt/vouchervault/app/myapp/static/
disable-logging = true
buffer-size=32768
EOF
chown www-data:www-data /opt/vouchervault/uwsgi.ini
启动(守护进程方式,日志写入 logs 目录):
runuser -u www-data -- /opt/vouchervault/venv/bin/uwsgi --ini /opt/vouchervault/uwsgi.ini \
--die-on-term --daemonize /opt/vouchervault/logs/uwsgi.log
tail -8 /opt/vouchervault/logs/uwsgi.log
[uwsgi-static] added mapping for /static => /opt/vouchervault/app/myapp/static/
*** Operational MODE: preforking+threaded ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x5e922d50f7a0 pid: 6541 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 6541)
spawned uWSGI worker 1 (pid: 6543, cores: 2)
spawned uWSGI worker 2 (pid: 6544, cores: 2)
十、功能验证
10.1 HTTP 冒烟测试
curl -s -o /dev/null -w 'GET / -> %{http_code}\n' http://127.0.0.1:8000/
GET / -> 302
根路径按浏览器语言重定向到 /en/ 等语言前缀路径。登录页(Django 自带认证挂在 accounts/ 下):
curl -s -o /dev/null -w 'GET /en/accounts/login/ -> %{http_code}\n' http://127.0.0.1:8000/en/accounts/login/
GET /en/accounts/login/ -> 200
curl -s http://127.0.0.1:8000/en/accounts/login/ | grep -o "<title>[^<]*"
<title>VoucherVault
10.2 会话登录与 REST API 冒烟
模拟表单登录(先取 CSRF Cookie 与令牌,再提交账号密码):
rm -f /tmp/cj
TOKEN=$(curl -s -c /tmp/cj http://127.0.0.1:8000/en/accounts/login/ | grep -o 'csrfmiddlewaretoken" value="[^"]*' | head -1 | cut -d'"' -f3)
curl -s -b /tmp/cj -c /tmp/cj -o /dev/null -w 'POST login -> %{http_code} redirect=%{redirect_url}\n' \
--data-urlencode "csrfmiddlewaretoken=$TOKEN" \
--data-urlencode "username=admin" \
--data-urlencode "password=q1w2e3r4" \
http://127.0.0.1:8000/en/accounts/login/
POST login -> 302 redirect=http://127.0.0.1:8000/
302 回首页即登录成功。REST API 走 Bearer Token 而非会话认证(详见本专栏《VoucherVault API 使用详解》),未带 Token 时返回:
curl -s -w '\nHTTP %{http_code}\n' http://127.0.0.1:8000/en/api/get/stats
Unauthorized. Invalid or missing authorization token.
HTTP 403
这同时证明 API 鉴权拦截在生效。
十一、Nginx 反向代理(生产推荐)
uWSGI 以 http-socket 监听 8000,前面挂 Nginx 做 TLS 终止与静态文件直发。创建 /etc/nginx/sites-available/vouchervault:
server {
listen 80;
server_name vouchervault.example.com; # 改成你的域名,并同步修改 .env 的 DOMAIN
client_max_body_size 20M;
location /static/ {
alias /opt/vouchervault/app/myapp/static/;
expires 7d;
}
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}
启用并重载:
ln -sf /etc/nginx/sites-available/vouchervault /etc/nginx/sites-enabled/vouchervault
rm -f /etc/nginx/sites-enabled/default
nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
systemctl reload nginx
通过 Nginx(80 端口)验证代理与静态直发:
curl -s -o /dev/null -w 'GET / -> %{http_code} redirect=%{redirect_url}\n' http://127.0.0.1/
GET / -> 302 redirect=http://127.0.0.1/en/
curl -s -o /dev/null -w 'GET /static/css/django-pwa-app.css -> %{http_code}\n' http://127.0.0.1/static/css/django-pwa-app.css
GET /static/css/django-pwa-app.css -> 200
⚠️ 上线 HTTPS 后,把
.env中SECURE_COOKIES改为True并重启 uWSGI,开启安全 Cookie 与 HSTS。
十二、systemd 托管(生产建议)
手动 --daemonize 适合安装验证,生产建议交给 systemd。以下三个单元文件按上文路径编写,可直接落盘。
/etc/systemd/system/vouchervault-web.service:
[Unit]
Description=VoucherVault uWSGI application server
After=network.target redis-server.service
[Service]
Type=notify
User=www-data
Group=www-data
WorkingDirectory=/opt/vouchervault/app
ExecStart=/opt/vouchervault/venv/bin/uwsgi --ini /opt/vouchervault/uwsgi.ini --die-on-term
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
/etc/systemd/system/vouchervault-celery.service:
[Unit]
Description=VoucherVault Celery worker
After=network.target redis-server.service
[Service]
Type=forking
User=www-data
Group=www-data
WorkingDirectory=/opt/vouchervault/app
ExecStart=/opt/vouchervault/venv/bin/celery -A myproject worker -l info --detach --logfile=/opt/vouchervault/logs/celery-worker.log --pidfile=/opt/vouchervault/logs/celery-worker.pid
PIDFile=/opt/vouchervault/logs/celery-worker.pid
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
/etc/systemd/system/vouchervault-celery-beat.service:
[Unit]
Description=VoucherVault Celery beat scheduler
After=network.target redis-server.service
[Service]
Type=forking
User=www-data
Group=www-data
WorkingDirectory=/opt/vouchervault/app
ExecStart=/opt/vouchervault/venv/bin/celery -A myproject beat -l info --detach --scheduler django_celery_beat.schedulers:DatabaseScheduler --logfile=/opt/vouchervault/logs/celery-beat.log --pidfile=/opt/vouchervault/logs/celery-beat.pid
PIDFile=/opt/vouchervault/logs/celery-beat.pid
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
启用(每行一个单元):
systemctl daemon-reload
systemctl enable vouchervault-web.service
systemctl enable vouchervault-celery.service
systemctl enable vouchervault-celery-beat.service
systemctl start vouchervault-web.service
systemctl start vouchervault-celery.service
systemctl start vouchervault-celery-beat.service
注:uWSGI 单元使用
Type=notify,需要 uWSGI 的 systemd 通知支持;若启动报 notify 错误,把Type=notify改为Type=simple并去掉--daemonize(上文单元文件默认未带该参数,直接以前台方式运行)。
十三、进阶:切换 PostgreSQL
默认 SQLite 适合个人/家庭;多人共用建议 PostgreSQL。差异点:
- 安装并初始化 PostgreSQL 实例,建库建账号(依赖包
libpq-dev、postgresql-client与 Python 侧psycopg2-binary已在上文装好); .env追加:DB_ENGINE=postgres、POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB、POSTGRES_HOST、POSTGRES_PORT;- 重新执行第七节迁移命令,再启动服务。
十四、排障实录
1. curl 模拟登录返回 403「CSRF verification failed」
完整报错页含 Reason given for failure: CSRF token from POST has incorrect length.。原因是 POST 体里的 csrfmiddlewaretoken 与 Cookie 不匹配(提取失败或没带 Cookie)。解法:务必 curl -c 存 Cookie、-b 带 Cookie,用 --data-urlencode 原样提交从登录页提取的 64 位令牌。若换用域名访问,还要确认 .env 的 DOMAIN 与访问地址一致——它决定 CSRF_TRUSTED_ORIGINS。
2. 调用 /api/get/stats 返回 403「Unauthorized. Invalid or missing authorization token.」
该端点不做会话认证,要求 Authorization: Bearer <api_token> 头;Token 存于后台「App settings」(模型 AppSettings.api_token,UUID 格式,可在管理后台重新生成)。详见本专栏《VoucherVault API 使用详解》。
3. Celery Beat 启动了但到期提醒从不触发
检查启动参数是否漏了 --scheduler django_celery_beat.schedulers:DatabaseScheduler。默认 scheduler 读本地文件而非数据库,create_default_periodic_tasks 创建的任务不会被调度。
4. 静态文件 404
STATIC_ROOT 是 myapp/static/(不是常见的 static/),修改源码或升级版本后必须重跑 collectstatic;uWSGI 的 static-map 与 Nginx 的 alias 都要指向这个目录。
5. uWSGI 日志出现 !!! no internal routing support, rebuild with pcre support !!!
Ubuntu 仓库版 uWSGI 或未启用 PCRE 的编译产物的提示,对本案无影响(未用到内部路由),可忽略。
十五、参考链接
- 官方仓库:https://github.com/l4rm4nd/VoucherVault (查询日期 2026-10-25)
- 官方 Wiki - 安装篇:https://github.com/l4rm4nd/VoucherVault/wiki/01-%E2%80%90-Installation (查询日期 2026-10-25)
- 站内:《VoucherVault 介绍与选型指南》
- 站内:《VoucherVault Docker Compose 部署指南》(本专栏)
- 站内:《VoucherVault 配置参数详解》(本专栏)
- 站内:《VoucherVault API 使用详解》(本专栏)