Hướng dẫn triển khai Ghost CMS 6 trên Ubuntu 24.04 khi máy chủ đã cài sẵn aaPanel, Nginx và MariaDB. Ghost được chạy bằng systemd, đặt toàn bộ project trong /www/wwwroot/<domain>, còn aaPanel chịu trách nhiệm quản lý Nginx, domain và SSL.Quy ước user trong bài viết
Trong quá trình triển khai, bạn sẽ sử dụng ba loại tài khoản chính:
| User | Khi nào sử dụng | Nhiệm vụ chính |
|---|---|---|
root | Các thao tác quản trị hệ thống | Tạo user, tạo database, sửa quyền, cài package hệ thống, cấu hình sudo, Nginx và systemd |
ghost | Các thao tác cài đặt và quản trị Ghost | Chạy ghost install, ghost setup, ghost ls, ghost log, ghost restart |
User database example_com | Chỉ dùng bên trong MariaDB | Ghost sử dụng tài khoản này để kết nối database, không dùng để đăng nhập Linux |
Nguyên tắc quan trọng
- Không chạy
ghost install,ghost setuphoặc các lệnh Ghost-CLI production bằngroot. - Dùng
rootkhi cần thay đổi hệ thống, quyền file, user, sudo, Nginx hoặc systemd. - Dùng user
ghostkhi thao tác với source code, cấu hình và Ghost-CLI. - Dùng tài khoản database
example_comchỉ để kết nối MariaDB. - Khi cần chạy lệnh hệ thống từ user
ghost, dùngsudo. - Khi cần chuyển từ
rootsangghost, dùng:
su - ghost
- Khi cần quay lại
root, dùng:
exit
Mô hình triển khai hoàn chỉnh:
Internet
│
▼
Domain
│
▼
Nginx aaPanel :80 / :443
│
│ Reverse Proxy
▼
Ghost CMS :2369
│
▼
MariaDB
Trong bài viết này, sử dụng domain mẫu example.com và các thông tin minh họa sau:
Domain: example.com
Project: /www/wwwroot/example.com
Node.js: v22.x
Ghost: 6.59.0
Database: example_com
DB User: example_com
Backend: 127.0.0.1:2369
OS: Ubuntu 24.04
Bạn cần thayexample.com,example_comvà các giá trị liên quan bằng domain, tên database và username thực tế của mình khi triển khai.
Tổng quan
Nếu bạn đã quen triển khai website bằng aaPanel nhưng muốn chạy Ghost CMS thay vì WordPress/PHP, cách triển khai sẽ hơi khác.
Ghost không chạy bằng PHP-FPM và cũng không nên chạy bằng PM2 trong mô hình production này. Thay vào đó, Ghost có thể chạy bằng Node.js + Ghost-CLI + systemd, trong khi Nginx của aaPanel đóng vai trò reverse proxy.
aaPanel quản lý:
Domain
Nginx
SSL
Virtual host
Ghost quản lý:
Application
Content
Config
Systemd service
MariaDB quản lý:
Database
Database user
Database permissions
1. Kiến trúc sau khi cài Ghost
Sau khi hoàn tất, hệ thống sẽ có cấu trúc gần như sau:
/www/wwwroot/example.com/
├── .ghost-cli
├── .pnpm-store/
├── config.production.json
├── config.production.json.bak
├── content/
├── current -> /www/wwwroot/example.com/versions/6.59.0
└── versions/
└── 6.59.0/
Ghost chạy bằng:
systemd
với user:
ghost
Nginx của aaPanel sẽ proxy:
https://example.com
↓
http://127.0.0.1:2369
Điểm quan trọng là HTTPS được xử lý tại Nginx, không phải tại Ghost.
2. Yêu cầu trước khi cài
Server nên có sẵn:
- Ubuntu 24.04 LTS
- aaPanel
- Nginx
- MariaDB
- Node.js 22.x
- Domain đã trỏ về VPS
Các lệnh kiểm tra hệ thống có thể chạy bằng root:
cat /etc/os-release
Kiểm tra Node.js:
node -v
npm -v
Kiểm tra MariaDB:
mysql --version
Kiểm tra Nginx:
nginx -v
Ví dụ môi trường:
Ubuntu 24.04.4 LTS
Node.js 22.23.2
npm 10.9.8
MariaDB 10.11.10
Nginx 1.24.0
Nếu đang đăng nhập bằng user thường, chuyển sang root trước khi thực hiện các bước quản trị hệ thống:sudo -i
3. Tạo database cho Ghost
User cần sử dụng
- Linux user:
root - MariaDB user:
rootđể tạo database và tài khoản Ghost - Ghost database user:
example_comđể Ghost kết nối database sau này
Ví dụ tạo:
Database: example_com
Username: example_com
Password: YOUR_DATABASE_PASSWORD
Đăng nhập MariaDB bằng tài khoản MariaDB root. Đây là root của MariaDB, không nhất thiết giống hoàn toàn với user root của Linux:
mysql -u root -p
Tạo database:
CREATE DATABASE example_com;
Tạo user:
CREATE USER 'example_com'@'localhost'
IDENTIFIED BY 'YOUR_DATABASE_PASSWORD';
Cấp quyền:
GRANT ALL PRIVILEGES
ON example_com.*
TO 'example_com'@'localhost';
Áp dụng:
FLUSH PRIVILEGES;
Kiểm tra:
SHOW DATABASES;
Thoát:
EXIT;
Kiểm tra tài khoản database
Trước khi cài Ghost, nên test luôn bằng user database example_com:
mysql -u example_com -p -h 127.0.0.1 example_com
Nếu đăng nhập thành công:
SELECT VERSION();
và:
SHOW TABLES;
Nếu database mới tạo trả:
Empty set
thì hoàn toàn bình thường.
Thoát khỏi MariaDB:
EXIT;
Không dùng user Linuxghostđể đăng nhập MariaDB nếu bạn chưa tạo một tài khoản MariaDB có tên đó. Ghost sẽ kết nối bằng database userexample_com.
4. Node.js 22 trên aaPanel
User cần sử dụng
Các thao tác kiểm tra và cấu hình PATH có thể thực hiện bằng root. Tuy nhiên, cần kiểm tra lại Node.js bằng cả root và ghost, vì mỗi user có thể có PATH khác nhau.
Trong trường hợp aaPanel đã có Node.js Manager và Node 22 được cài tại:
/www/server/nodejs/v22.23.2
cần đảm bảo shell sử dụng đúng Node này.
Kiểm tra bằng root:
which node
which npm
Mục tiêu:
/www/server/nodejs/v22.23.2/bin/node
/www/server/nodejs/v22.23.2/bin/npm
Thêm Node vào PATH cho phiên shell hiện tại:
export PATH="/www/server/nodejs/v22.23.2/bin:$PATH"
Để áp dụng lâu dài cho root:
echo 'export PATH="/www/server/nodejs/v22.23.2/bin:$PATH"' >> /root/.bashrc
Sau đó:
source /root/.bashrc
Kiểm tra:
node -v
npm -v
Sau khi tạo user ghost, cần cấu hình PATH tương tự cho user này. Phần đó được thực hiện ở mục 9.
Lưu ý: Không cần tạo một Node Project/PM2 Project trong aaPanel cho Ghost. Ghost production sẽ chạy bằng systemd.
5. Tạo thư mục project Ghost
User cần sử dụng
Dùng root để tạo thư mục website và sau đó chuyển quyền sở hữu cho user ghost.
Tạo thư mục:
mkdir -p /www/wwwroot/example.com
Kiểm tra:
ls -la /www/wwwroot/example.com
Ban đầu thư mục nên trống.
Không chạyghost installtrong thư mục này bằngroot. Sau khi tạo userghostvà cấp quyền, các lệnh Ghost-CLI sẽ được chạy bằng userghost.
6. Tạo user riêng cho Ghost
User cần sử dụng
Dùng root.
Ghost-CLI không cho phép chạy lệnh production bằng root. Vì vậy, cần tạo một Linux user riêng tên ghost.
Tạo user:
useradd --system --create-home --shell /bin/bash ghost
Kiểm tra:
id ghost
Cho Ghost sở hữu project:
chown -R ghost:ghost /www/wwwroot/example.com
Kiểm tra:
ls -ld /www/wwwroot/example.com
Kết quả nên tương tự:
drwxr-xr-x ghost ghost /www/wwwroot/example.com
Từ thời điểm này, các lệnh liên quan trực tiếp đến Ghost nên chạy bằng userghost, không chạy bằngroot.
7. Cài Ghost-CLI
User cần sử dụng
Cài Ghost-CLI bằng npm có thể thực hiện bằng root, vì đây là cài đặt global vào thư mục Node.js của aaPanel.
Cài Ghost-CLI:
npm install ghost-cli@latest -g
Sau đó kiểm tra:
ghost version
Ví dụ:
Ghost-CLI version: 1.32.2
Việc cài Ghost-CLI bằngrootkhông có nghĩa là được phép chạyghost installbằngroot. Chỉ cài package global bằngroot; quá trình cài Ghost production phải chạy bằng userghost.
Xử lý lỗi ghost: command not found
Nếu npm install thành công nhưng:
ghost version
lại báo:
Command 'ghost' not found
thì kiểm tra:
npm config get prefix
Nếu kết quả:
/www/server/nodejs/v22.23.2
thì executable của Ghost nằm tại:
/www/server/nodejs/v22.23.2/bin/ghost
Thêm PATH cho phiên shell hiện tại:
export PATH="/www/server/nodejs/v22.23.2/bin:$PATH"
Sau đó:
which ghost
8. Xử lý quyền npm cache của aaPanel
User cần sử dụng
- Kiểm tra và sửa quyền:
root - Kiểm tra cache sau khi sửa:
ghost
Một vấn đề có thể gặp khi aaPanel trước đó đã sử dụng npm bằng root là lỗi:
EACCES
Your cache folder contains root-owned files
Ví dụ npm cache nằm ở:
/www/server/nodejs/cache
Kiểm tra user Ghost:
id ghost
Kiểm tra quyền cache:
ls -ld /www/server/nodejs/cache
Nếu thư mục cache đang thuộc về root hoặc chứa file do root tạo, sửa quyền cho user ghost.
Có thể lấy UID và GID tự động:
GHOST_UID=$(id -u ghost)
GHOST_GID=$(id -g ghost)
chown -R "$GHOST_UID:$GHOST_GID" /www/server/nodejs/cache
Hoặc nếu UID/GID thực tế là:
uid=997
gid=988
thì có thể dùng:
chown -R 997:988 /www/server/nodejs/cache
Kiểm tra:
ls -ld /www/server/nodejs/cache
Sau đó chuyển sang user ghost:
su - ghost
Kiểm tra npm cache:
npm cache verify
Quay lại root khi cần:
exit
9. Đảm bảo user Ghost sử dụng đúng Node.js
User cần sử dụng
Thực hiện phần này bằng user ghost.
Chuyển sang user:
su - ghost
Kiểm tra:
which node
node -v
which npm
npm -v
which ghost
Các đường dẫn nên trỏ về Node của aaPanel:
/www/server/nodejs/v22.23.2/bin/node
/www/server/nodejs/v22.23.2/bin/npm
/www/server/nodejs/v22.23.2/bin/ghost
Nếu chưa đúng:
export PATH="/www/server/nodejs/v22.23.2/bin:$PATH"
Để lưu PATH lâu dài cho user ghost:
echo 'export PATH="/www/server/nodejs/v22.23.2/bin:$PATH"' >> ~/.bashrc
Nạp lại cấu hình:
source ~/.bashrc
Kiểm tra lại:
which node
node -v
which npm
npm -v
which ghost
Từ đây trở đi, bạn đang ở userghost. Các lệnhcd,ghost install,ghost setup,ghost lsvà các lệnh quản trị Ghost nên chạy trong phiên shell này.
10. Cài Ghost CMS
User cần sử dụng
Dùng user ghost.
Đi tới thư mục:
cd /www/wwwroot/example.com
Kiểm tra:
pwd
Kết quả:
/www/wwwroot/example.com
Cài Ghost:
ghost install 6.59.0
Bạn có thể thay 6.59.0 bằng phiên bản Ghost 6.x phù hợp tại thời điểm triển khai.
Không chạy lệnh trên bằngroot. Nếu đang ởroot, chuyển sang userghost:
su - ghost
11. Điền thông tin trong Ghost-CLI
User cần sử dụng
Vẫn dùng user ghost.
Ghost-CLI sẽ hỏi các thông tin.
Blog URL
https://example.com
MySQL hostname
127.0.0.1
MySQL username
example_com
Đây là user của MariaDB, không phải Linux user ghost.
MySQL password
Nhập password database của user:
example_com
Ghost database name
example_com
Ghost sẽ dùng thông tin này để kết nối MariaDB.
12. Chọn Nginx = No
User cần sử dụng
Dùng user ghost trong quá trình Ghost-CLI hỏi cấu hình.
Ghost-CLI có thể hỏi:
Do you wish to set up Nginx?
Chọn:
No
Lý do: Nginx đã được aaPanel quản lý.
Không nên để Ghost-CLI tự sửa cấu hình /etc/nginx/ trong trường hợp aaPanel đã quản lý virtual host.
Nếu cần chỉnh Nginx, chuyển sang root sau khi thoát khỏi Ghost-CLI:
exit
hoặc mở một phiên SSH khác bằng root.
13. SSL = No
User cần sử dụng
Trong quá trình Ghost-CLI hỏi SSL, vẫn dùng user ghost.
Nếu Ghost-CLI hỏi setup SSL:
No
SSL sẽ được cấp và quản lý bằng aaPanel.
Mô hình:
Browser
↓ HTTPS
Nginx aaPanel
↓ HTTP
Ghost
Không cần cấp SSL trực tiếp trong Ghost-CLI khi aaPanel đã quản lý SSL cho domain.
14. Systemd = Yes
User cần sử dụng
Trong quá trình cài đặt, dùng user ghost.
Khi Ghost hỏi:
Do you want to set up Systemd?
chọn:
Yes
Ghost sẽ được quản lý bằng systemd.
Ví dụ service:
ghost_example-com.service
Sau khi Ghost-CLI tạo service, các lệnh kiểm tra systemd cần quyền hệ thống. Bạn có thể chạy bằng root hoặc dùng sudo từ user ghost nếu đã cấp sudo:
sudo systemctl status ghost_example-com
Hoặc chuyển sang root:
exit
rồi chạy:
systemctl status ghost_example-com
15. Cấp sudo cho user Ghost
User cần sử dụng
Dùng root.
Trong quá trình ghost setup, Ghost-CLI có thể cần sudo để:
- thay đổi owner thư mục
- tạo systemd service
- reload systemd
- start/enable service
Một cách đơn giản là tạo sudo rule riêng.
Nếu hiện tại đang ở user ghost, quay lại root:
exit
Tạo file sudoers:
cat > /etc/sudoers.d/ghost <<'EOF'
ghost ALL=(ALL) NOPASSWD: ALL
EOF
Sau đó:
chmod 440 /etc/sudoers.d/ghost
Kiểm tra syntax:
visudo -cf /etc/sudoers.d/ghost
Nếu trả:
parsed OK
thì chuyển sang user Ghost:
su - ghost
Test quyền sudo:
sudo -n whoami
Kết quả:
root
Cấu hình này được thực hiện bằngroot, nhưng các lệnh Ghost-CLI sau đó vẫn chạy bằng userghost. Userghostchỉ gọisudokhi cần thực hiện thao tác hệ thống.
Sau khi triển khai thành công, có thể siết chặt sudo permission hơn nếu server yêu cầu hardening cao. Cấu hình trên ưu tiên sự đơn giản và tương thích với Ghost-CLI.
16. Nếu Ghost cài xong nhưng ghost setup chưa hoàn tất
User cần sử dụng
Chạy ghost setup bằng user ghost.
Trường hợp Ghost đã tải source và cài application nhưng fail ở systemd do sudo, không cần chạy lại ****ghost install.
Nếu đang ở root, chuyển sang ghost:
su - ghost
Chạy:
cd /www/wwwroot/example.com
ghost setup
Sau khi sudo hoạt động, Ghost-CLI có thể tiếp tục hoàn tất:
✔ Setting up "ghost" system user
✔ Setting up Systemd
✔ Starting Ghost
Các thao tác mà Ghost-CLI cần quyền hệ thống sẽ tự gọisudo. Không cần chuyển toàn bộ quá trình sangroot.
17. Kiểm tra Ghost đã chạy
User cần sử dụng
ghost ls: userghostsystemctl status:roothoặc userghostcó sudo
Dùng user ghost:
cd /www/wwwroot/example.com
ghost ls
Ví dụ:
Name Location Version Status
example-com /www/wwwroot/example.com 6.59.0 running
Kiểm tra systemd bằng sudo:
sudo systemctl status ghost_example-com --no-pager
Hoặc chuyển sang root:
exit
systemctl status ghost_example-com --no-pager
Mục tiêu:
Active: active (running)
18. Kiểm tra port Ghost
User cần sử dụng
Có thể chạy bằng ghost hoặc root. Nếu cần xem đầy đủ process, nên dùng root.
Kiểm tra:
ss -lntp | grep 2369
Nếu thấy:
LISTEN 127.0.0.1:2369
thì Ghost đang lắng nghe đúng.
Port Ghost chỉ bind trên 127.0.0.1, vì vậy không mở trực tiếp port này ra Internet. Người dùng bên ngoài chỉ truy cập thông qua Nginx.19. Kiểm tra trực tiếp Ghost trước khi cấu hình Nginx
User cần sử dụng
Có thể chạy bằng user ghost hoặc root.
Test HTTP:
curl -I http://127.0.0.1:2369
Ghost có thể trả:
HTTP/1.1 301 Moved Permanently
với:
Location: https://example.com/
Điều này là bình thường khi URL chính thức của Ghost là HTTPS.
Không nên dùng curl https://127.0.0.1:2369 nếu backend Ghost đang phục vụ HTTP. HTTPS sẽ do Nginx xử lý.20. Kiểm tra config.production.json
User cần sử dụng
File này thuộc về user ghost, vì vậy nên đọc hoặc chỉnh sửa bằng user ghost.
File quan trọng nhất của Ghost:
/www/wwwroot/example.com/config.production.json
Xem cấu hình server:
cd /www/wwwroot/example.com
jq '.server' config.production.json
Ví dụ:
{
"port": 2369,
"host": "127.0.0.1"
}
Kiểm tra URL:
jq '.url' config.production.json
Kết quả:
"https://example.com"
Kiểm tra database mà không lộ password:
jq '{
client: .database.client,
host: .database.connection.host,
user: .database.connection.user,
database: .database.connection.database
}' config.production.json
Nếu file không đọc được do sai quyền, không nên tùy tiện dùngchmod 777. Hãy kiểm tra owner và group bằngls -l, sau đó sửa quyền bằngrootnếu cần.
21. Cấu hình staffDeviceVerification
User cần sử dụng
- Backup và chỉnh sửa file: user
ghost - Nếu gặp lỗi quyền:
root - Restart service: user
ghostdùngsudo, hoặcroot
Một thiết lập quan trọng sau khi cài Ghost là:
staffDeviceVerification
Trong một số môi trường, Ghost có thể yêu cầu xác minh thiết bị khi đăng nhập tài khoản Staff từ thiết bị mới. Nếu muốn tắt cơ chế này, mở:
/www/wwwroot/example.com/config.production.json
Tìm:
"staffDeviceVerification": true
Đổi thành:
"staffDeviceVerification": false
Có thể dùng jq để sửa
Đảm bảo đang ở user ghost:
whoami
Kết quả phải là:
ghost
Backup file trước:
cp /www/wwwroot/example.com/config.production.json \
/www/wwwroot/example.com/config.production.json.bak
Sau đó:
jq '.staffDeviceVerification = false' \
/www/wwwroot/example.com/config.production.json \
> /tmp/config.production.json \
&& mv /tmp/config.production.json \
/www/wwwroot/example.com/config.production.json
Kiểm tra:
jq '.staffDeviceVerification' \
/www/wwwroot/example.com/config.production.json
Kết quả phải là:
false
Nếu lệnhmvhoặc ghi file bị lỗi quyền, chuyển sangrootđể kiểm tra owner của file. Không nên đổi toàn bộ thư mục sang quyền ghi cho mọi user.
22. Restart Ghost sau khi thay đổi config
User cần sử dụng
Có thể dùng user ghost với sudo:
sudo systemctl restart ghost_example-com
Kiểm tra:
sudo systemctl status ghost_example-com --no-pager
Hoặc dùng lệnh Ghost-CLI bằng user ghost:
cd /www/wwwroot/example.com
ghost restart
Nếu vẫn:
running (production)
thì cấu hình đã được áp dụng.
Nếu đang ở root, có thể chạy trực tiếp:systemctl restart ghost_example-com
Lưu ý bảo mật: Tắt staffDeviceVerification làm giảm một lớp bảo vệ trong quá trình đăng nhập Staff. Chỉ nên sử dụng khi bạn hiểu rõ nhu cầu vận hành và đang có các lớp bảo vệ khác như mật khẩu mạnh, HTTPS và giới hạn truy cập khu vực quản trị.23. Tạo website trên aaPanel
User cần sử dụng
Thực hiện trong giao diện aaPanel bằng tài khoản quản trị aaPanel. Trên hệ thống, các thao tác tương ứng thường được aaPanel thực hiện với quyền hệ thống.
Trong aaPanel:
Website
→ Add Site
Domain:
example.com
Thư mục:
/www/wwwroot/example.com
Ghost không cần PHP.
Không cần tạo:
PM2 Project
Không cần:
npm start
Không cần:
pm2 start
aaPanel chỉ tạo website và virtual host. Application Ghost vẫn do user ghost chạy bằng systemd.24. Cấu hình Nginx reverse proxy trên aaPanel
User cần sử dụng
Thực hiện trong giao diện aaPanel hoặc chỉnh file Nginx bằng root.
Trong website:
aaPanel
→ Website
→ example.com
→ Config
Các phần SSL của aaPanel có thể giữ nguyên.
Bạn cần proxy request từ Nginx tới:
http://127.0.0.1:2369
24.1. Cấu hình gốc cần thay thế
aaPanel thường tạo sẵn một cấu hình virtual host cho website. Cấu hình gốc có thể khác nhau tùy phiên bản aaPanel, nhưng thường có dạng tương tự:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /www/wwwroot/example.com;
index index.php index.html index.htm;
include /www/server/panel/vhost/rewrite/example.com.conf;
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi-82.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 12h;
access_log off;
}
access_log /www/wwwlogs/example.com.log;
error_log /www/wwwlogs/example.com.error.log;
}
Nếu website đã bật SSL, aaPanel thường có thêm một block HTTPS tương tự:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
root /www/wwwroot/example.com;
index index.php index.html index.htm;
ssl_certificate /www/server/panel/vhost/cert/example.com/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/example.com/privkey.pem;
include /www/server/panel/vhost/rewrite/example.com.conf;
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi-82.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 12h;
access_log off;
}
access_log /www/wwwlogs/example.com.log;
error_log /www/wwwlogs/example.com.error.log;
}
Đây chỉ là cấu hình minh họa. Không nên sao chép nguyên xi các đường dẫn PHP, socket PHP hoặc đường dẫn chứng chỉ nếu chúng không đúng với server của bạn.
24.2. Cấu hình thay thế cho Ghost
Với Ghost, bạn không cần root, index, PHP-FPM hoặc các block xử lý static file theo kiểu website PHP. Hãy thay phần xử lý request của virtual host bằng cấu hình reverse proxy tới Ghost.
Cấu hình HTTP
Nếu chưa bật SSL hoặc muốn kiểm tra HTTP trước, dùng:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2369;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_redirect off;
}
access_log /www/wwwlogs/example.com.log;
error_log /www/wwwlogs/example.com.error.log;
}
Cấu hình HTTPS đầy đủ
Sau khi đã cấp SSL trong aaPanel, dùng cấu hình tương tự sau:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /www/server/panel/vhost/cert/example.com/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:2369;
proxy_http_version 1.1;
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 https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_redirect off;
}
access_log /www/wwwlogs/example.com.log;
error_log /www/wwwlogs/example.com.error.log;
}
Đường dẫn chứng chỉ trong ví dụ có thể khác với server thực tế. Hãy lấy đúng đường dẫn certificate và private key do aaPanel hiển thị trong phần SSL của website.
Cấu hình thay thế khi aaPanel đã có sẵn block SSL
Nếu aaPanel đã tạo sẵn block server cho HTTPS, bạn không nhất thiết phải xóa toàn bộ file. Chỉ cần:
- Giữ lại các dòng
listen,server_name,ssl_certificate,ssl_certificate_key,access_logvàerror_log. - Xóa hoặc vô hiệu hóa các block PHP-FPM.
- Xóa hoặc vô hiệu hóa các block static file không cần thiết.
- Thay phần xử lý request bằng block sau:
location / {
proxy_pass http://127.0.0.1:2369;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_redirect off;
}
24.3. Cấu hình qua giao diện aaPanel
Nếu chỉnh trong giao diện aaPanel:
- Vào
Website. - Chọn
example.com. - Chọn
Config. - Sao lưu cấu hình hiện tại.
- Tìm block
serverđang phục vụ domain. - Giữ lại cấu hình SSL nếu đã bật HTTPS.
- Xóa các block PHP-FPM và các block xử lý static file không cần thiết.
- Thêm hoặc thay thế bằng block
location /reverse proxy. - Lưu cấu hình.
- Kiểm tra cấu hình Nginx.
- Reload Nginx.
24.4. Cấu hình bằng file với root
Nếu chỉnh trực tiếp bằng SSH, trước tiên xác định file virtual host:
grep -R "server_name.*example.com" /www/server/panel/vhost/nginx/ -n
Sao lưu file trước khi sửa:
cp /www/server/panel/vhost/nginx/example.com.conf \
/www/server/panel/vhost/nginx/example.com.conf.bak
Mở file:
nano /www/server/panel/vhost/nginx/example.com.conf
Sau khi chỉnh xong, kiểm tra:
nginx -t
Nếu hợp lệ:
systemctl reload nginx
aaPanel có thể ghi đè file cấu hình khi bạn thay đổi website, SSL hoặc một số thiết lập trong giao diện. Vì vậy, nên ưu tiên cấu hình reverse proxy trong phần Config của chính website và kiểm tra lại sau mỗi lần aaPanel cập nhật virtual host.
24.5. Các lỗi cấu hình thường gặp ở mục này
Dùng sai giao thức backend
Đúng:
proxy_pass http://127.0.0.1:2369;
Sai:
proxy_pass https://127.0.0.1:2369;
Ghost thường phục vụ HTTP nội bộ, còn HTTPS được xử lý tại Nginx.
Dùng sai port
Kiểm tra port thực tế trong Ghost:
jq '.server' /www/wwwroot/example.com/config.production.json
Nếu Ghost dùng port khác, ví dụ 2371, phải sửa:
proxy_pass http://127.0.0.1:2371;
Giữ lại cấu hình PHP-FPM
Không nên giữ các block như:
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi-82.sock;
}
Ghost không chạy bằng PHP-FPM.
Có nhiều block location /
Trong cùng một server block, chỉ nên có một block chính:
location / {
proxy_pass http://127.0.0.1:2369;
}
Nếu có nhiều block location /, Nginx có thể xử lý không đúng như mong muốn.
Thiếu header proxy
Tối thiểu nên có:
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;
Các header này giúp Ghost nhận biết đúng domain, IP client và giao thức HTTPS ban đầu.
25. Không để aaPanel xử lý static file theo kiểu PHP
User cần sử dụng
Thực hiện trong aaPanel hoặc bằng root khi chỉnh file Nginx.
Nếu aaPanel tự tạo các block như:
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
hoặc:
location ~ .*\.(js|css)?$
cần xem xét bỏ các block này khi sử dụng Ghost làm reverse proxy toàn site.
Ghost nên nhận toàn bộ request thông qua:
location /
để application tự quản lý route, asset và admin.
26. SSL trên aaPanel
User cần sử dụng
Thực hiện trong giao diện aaPanel bằng tài khoản quản trị.
Trong:
Website
→ example.com
→ SSL
Cấp Let's Encrypt cho:
example.com
www.example.com
Sau đó có thể bật:
Force HTTPS
Mô hình lúc này:
https://example.com
│
▼
Nginx aaPanel :443
│
▼
http://127.0.0.1:2369
│
▼
Ghost CMS
27. Kiểm tra cấu hình Nginx
User cần sử dụng
Dùng root, vì Nginx là dịch vụ hệ thống.
Nếu đang ở user ghost, quay lại root:
exit
Kiểm tra:
nginx -t
Phải nhận:
syntax is ok
test is successful
Sau đó:
systemctl reload nginx
28. Kiểm tra website
User cần sử dụng
Có thể chạy bằng root, ghost hoặc user thường có quyền truy cập mạng.
Test từ VPS:
curl -I https://example.com
Kiểm tra trang admin:
curl -I https://example.com/ghost/
Mở trình duyệt:
https://example.com
Trang quản trị:
https://example.com/ghost/
29. Kiểm tra database sau khi cài
User cần sử dụng
- Lệnh kiểm tra database: có thể dùng
roothoặc user database - Ghost kết nối database bằng user
example_com - Không dùng Linux user
ghostthay cho database user
Trước khi cài Ghost:
SHOW TABLES;
có thể trả:
Empty set
Sau khi Ghost chạy, hãy kiểm tra bằng tài khoản database example_com:
mysql -u example_com -p -h 127.0.0.1 example_com \
-e "SHOW TABLES;"
Lúc này database phải có rất nhiều bảng Ghost.
30. Kiểm tra log khi website lỗi
Ghost log
User cần sử dụng
Chạy bằng user ghost:
cd /www/wwwroot/example.com
ghost log
Hoặc dùng journalctl bằng user ghost có sudo:
sudo journalctl -u ghost_example-com -n 100 --no-pager
Hoặc chuyển sang root:
exit
journalctl -u ghost_example-com -n 100 --no-pager
Nginx error log
User cần sử dụng
Dùng root hoặc user có quyền đọc file log:
tail -f /www/wwwlogs/example.com.error.log
Nginx access log
tail -f /www/wwwlogs/example.com.log
31. Các lỗi thường gặp
Lỗi ghost: command not found
User cần sử dụng
Kiểm tra bằng user đang gặp lỗi, thường là ghost.
Nguyên nhân thường là PATH của Node aaPanel.
Kiểm tra:
npm config get prefix
Nếu:
/www/server/nodejs/v22.23.2
thêm:
export PATH="/www/server/nodejs/v22.23.2/bin:$PATH"
Sau đó kiểm tra:
which ghost
Lỗi EACCES trong npm cache
User cần sử dụng
- Kiểm tra và sửa quyền:
root - Xác nhận lại cache:
ghost
Ví dụ:
npm error EACCES
/www/server/nodejs/cache/_cacache/...
Từ root, sửa quyền:
GHOST_UID=$(id -u ghost)
GHOST_GID=$(id -g ghost)
chown -R "$GHOST_UID:$GHOST_GID" /www/server/nodejs/cache
Sau đó chuyển sang ghost:
su - ghost
npm cache verify
Lỗi You can't run commands as the 'root' user
User cần sử dụng
Ghost-CLI production phải chạy bằng user ghost.
Không chạy Ghost-CLI production bằng root.
Chuyển sang:
su - ghost
Sau đó chạy lại lệnh cần thiết:
ghost setup
hoặc:
ghost ls
Lỗi Current directory is not empty
User cần sử dụng
Kiểm tra thư mục bằng root hoặc ghost.
Ghost-CLI yêu cầu thư mục cài đặt mới phải sạch.
Kiểm tra:
ls -la /www/wwwroot/example.com
Một file thử nghiệm như:
ghost-6.59.0.tgz
có thể khiến install bị từ chối.
Nếu cần xóa file, chỉ xóa bằng root khi chắc chắn file không cần thiết:
rm -f /www/wwwroot/example.com/ghost-6.59.0.tgz
Sau đó đảm bảo owner vẫn là ghost:
chown -R ghost:ghost /www/wwwroot/example.com
Lỗi ETARGET No matching version found
User cần sử dụng
Kiểm tra bằng user đang chạy npm, thường là ghost.
Trước tiên kiểm tra:
npm config get registry
Nên là:
https://registry.npmjs.org/
Sau đó:
npm view ghost version
npm view ghost@6.59.0 version
Có thể test trực tiếp:
npm pack ghost@6.59.0 --json
Nếu npm pack thành công nhưng Ghost-CLI thất bại, hãy kiểm tra environment của npm và Ghost-CLI trước khi hạ version Ghost.
Lỗi 502 Bad Gateway
User cần sử dụng
- Kiểm tra port và curl:
roothoặcghost - Kiểm tra systemd:
roothoặcghostcó sudo - Kiểm tra Nginx:
root
Kiểm tra:
ss -lntp | grep 2369
và:
curl -I http://127.0.0.1:2369
Nếu Ghost không chạy:
sudo systemctl status ghost_example-com
Nếu Ghost chạy nhưng Nginx trả 502, kiểm tra:
proxy_pass http://127.0.0.1:2369;
Lỗi SSL wrong version number
User cần sử dụng
Có thể chạy bằng root, ghost hoặc user thường.
Nếu chạy:
curl -k -I https://127.0.0.1:2369
và nhận:
SSL routines::wrong version number
không nhất thiết là Ghost bị lỗi.
Backend Ghost đang có thể phục vụ HTTP:
http://127.0.0.1:2369
Còn HTTPS xử lý tại Nginx:
https://example.com
32. Lệnh quản trị Ghost thường dùng
User cần sử dụng
Các lệnh Ghost-CLI chạy bằng user ghost.
Vào thư mục project:
su - ghost
cd /www/wwwroot/example.com
Kiểm tra instance:
ghost ls
Khởi động:
ghost start
Restart:
ghost restart
Dừng:
ghost stop
Xem log:
ghost log
Kiểm tra hệ thống:
ghost doctor
Cập nhật Ghost:
ghost update
Các lệnh trên không nên chạy bằng root.
Nếu cần kiểm tra service systemd trực tiếp:
sudo systemctl status ghost_example-com
Nếu cần thao tác Nginx hoặc service hệ thống, chuyển sang root:
exit
33. Checklist sau khi hoàn thành
[ ] Đã phân biệt rõ root, ghost và database user
[ ] Ubuntu 24.04
[ ] Node.js 22.x
[ ] npm hoạt động bằng root
[ ] npm hoạt động bằng user ghost
[ ] Ghost-CLI đã cài
[ ] User ghost đã tạo
[ ] Project nằm tại /www/wwwroot/<domain>
[ ] Owner project là ghost:ghost
[ ] Database Ghost đã tạo bằng MariaDB root
[ ] Database user đã tạo
[ ] Ghost kết nối bằng database user riêng
[ ] Ghost 6.x đã cài bằng user ghost
[ ] Nginx được Ghost-CLI bỏ qua
[ ] SSL được Ghost-CLI bỏ qua
[ ] systemd hoạt động
[ ] Ghost đang listening trên 127.0.0.1:<port>
[ ] config.production.json có URL chính xác
[ ] staffDeviceVerification đã kiểm tra theo nhu cầu
[ ] Ghost đã restart sau khi đổi config
[ ] Nginx aaPanel proxy tới Ghost
[ ] Đã thay cấu hình PHP-FPM bằng reverse proxy
[ ] Đã kiểm tra không có nhiều block location /
[ ] SSL Let's Encrypt hoạt động
[ ] https://domain.com mở được
[ ] https://domain.com/ghost/ mở được
[ ] Database Ghost có bảng
[ ] Nginx không trả 502
34. Cấu hình kiến trúc khuyến nghị
Đối với server đang sử dụng aaPanel, cấu trúc này đơn giản và dễ quản lý:
INTERNET
│
▼
example.com
│
▼
┌──────────────────┐
│ aaPanel Nginx │
│ :80 / :443 │
│ SSL / Domain │
└────────┬─────────┘
│
│ Reverse Proxy
│
▼
┌──────────────────┐
│ Ghost 6.x │
│ Node.js 22.x │
│ systemd │
│ User: ghost │
│ 127.0.0.1:2369 │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ MariaDB │
│ example_com │
│ DB User: │
│ example_com │
└──────────────────┘
Phân quyền trách nhiệm:
root
├── Tạo user ghost
├── Tạo database và database user
├── Sửa quyền file
├── Cấu hình sudo
├── Quản lý systemd
└── Quản lý Nginx
ghost
├── Ghost installation
├── Ghost-CLI
├── Content
├── Config
├── ghost ls
├── ghost log
└── ghost restart
example_com
└── Kết nối MariaDB từ Ghost
Đây là mô hình phù hợp khi muốn giữ aaPanel làm nơi quản lý domain + Nginx + SSL, nhưng để Ghost tự quản lý application bằng Node.js + systemd.
Kết luận
Cài Ghost CMS trên Ubuntu với aaPanel không khó, nhưng cần tránh trộn lẫn trách nhiệm giữa aaPanel, root, user ghost và tài khoản database.
Cách triển khai nên phân chia:
root
├── User hệ thống
├── Quyền file
├── Sudo
├── Systemd
└── Nginx
aaPanel
├── Domain
├── Nginx
└── SSL
ghost
├── Ghost installation
├── Content
├── Config
└── Ghost-CLI
MariaDB
└── Ghost database
Quan trọng nhất:
- Dùng
rootcho các thao tác quản trị hệ thống. - Dùng
ghostđể chạy Ghost-CLI và quản trị Ghost. - Dùng user database riêng như
example_comđể Ghost kết nối MariaDB. - Không chạy
ghost install,ghost setuphoặcghost updatebằngroot. - Không tạo PM2 Project để chạy Ghost.
- Không để Ghost-CLI tự sửa Nginx khi aaPanel đã quản lý Nginx.
- Không để Ghost-CLI tự cấp SSL nếu aaPanel đã quản lý SSL.
- Ghost chạy bằng user riêng
ghost. - Ghost chạy bằng systemd.
- Nginx reverse proxy tới
127.0.0.1:<Ghost_Port>. - Khi cấu hình Nginx, cần thay phần xử lý PHP-FPM bằng
location /reverse proxy tới đúng port Ghost. - Sau khi thay đổi
config.production.json, cần restart Ghost. - Kiểm tra
staffDeviceVerificationtheo nhu cầu vận hành và cân nhắc rủi ro bảo mật trước khi tắt.
Với mô hình này, bạn có thể triển khai nhiều website Ghost trên cùng một VPS bằng cách dùng các port localhost khác nhau, ví dụ:
example.com → 127.0.0.1:2369
site2.com → 127.0.0.1:2371
site3.com → 127.0.0.1:2372
Trong khi toàn bộ domain, SSL và reverse proxy vẫn được quản lý tập trung từ aaPanel.