Table of Contents
我最近因為想要練習寫文章,所以找了幾個部落格內容管理系統,最後選定使用 Grav 當作部落格網站。
1. 檢查與更新 VM 環境
我這邊選 Linode 因為本身喜歡簡單容易操作的介面,架設這個選最便宜的方案 1 CPU。
在購買 VM 後,SSH 進去的第一件事情就是檢查 VM 環境:
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal
這邊確定為 Ubuntu 20.04 LTS。
2. 安裝 PHP 和移除 Apache
把 apt 套件更新並且把系統自帶的 apache 移除,因為 nginx 在靜態檔案的處理速度比較快:
$ sudo apt update -y && sudo apt upgrade -y && sudo apt remove apache2* && sudo apt autoremove
接下來安裝 PHP 基本套件:
$ sudo apt install vim zip unzip nginx git php-fpm php-cli php-gd php-curl php-mbstring php-xml php-zip php-apcu
調整 php.ini — 把 cgi.fix_pathinfo 改為 1,這邊是因為有資安的問題(參考):
$ vim /etc/php/8.1/fpm/php.ini
cgi.fix_pathinfo=1
$ systemctl restart php8.1-fpm
3. 配置 PHP-FPM
$ cd /etc/php/8.1/fpm/pool.d
$ mv www.conf www.conf.bak
$ vim grav.conf
由於我們這邊是選擇 1 CPU 的:
[grav]
user = grav
group = grav
listen = /var/run/php/php7.4-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /
建立使用者:
$ adduser grav
在 Linux 中,使用
adduser grav指令時,通常會同時建立:
- 使用者
grav:這是你指定的使用者名稱。- 群組
grav:系統會自動建立一個與使用者名稱相同的群組。- 使用者
grav會被加到群組grav:新建立的使用者 grav 會被自動加入到剛建立的群組 grav。換句話說,使用者 grav 的預設主要群組是 grav。因此,當使用者 grav 建立檔案或目錄時,這些檔案或目錄的預設所屬群組也是 grav。
如果你希望使用不同的群組或不自動建立群組,你可以使用其他參數來自定義,例如使用
--ingroup選項。
切換使用者並建立網站目錄:
$ su - grav
$ mkdir -p www/html
$ cd www/html
測試 HTML 跟 PHP 的格式會不會成功:
$ touch index.html && echo 'hello world!' >> index.html;
# 測試 index.html
$ rm index.html
$ touch index.php && echo '<?php phpinfo();' >> index.php
# 測試 index.php
退出 grav 角色。
4. 配置 Nginx
$ exit
# 這邊會切換成 root
$ cd /etc/nginx/sites-available/
$ ls -al
default
$ vim grav
server {
# listen 80;
index index.html index.php;
## Begin - Server Info
root /home/grav/www/html;
server_name localhost;
## End - Server Info
## Begin - Index
# for subfolders, simply adjust:
# `location /subfolder {`
# and the rewrite to use `/subfolder/index.php`
location / {
try_files $uri $uri/ /index.php?$query_string;
}
## End - Index
## Begin - Security
# deny all direct access for these folders
location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; }
# deny running scripts inside core system folders
location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
# deny running scripts inside user folder
location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
# deny access to specific files in the root folder
location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; }
## End - Security
## Begin - PHP
location ~ \.php$ {
# Choose either a socket or TCP/IP address
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
# fastcgi_pass unix:/var/run/php5-fpm.sock; #legacy
# fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
## End - PHP
}
設定完成後 nginx -t 測試:
$ cd ../sites-enabled
$ ln -s ../sites-available/grav
$ rm 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 restart nginx
$ systemctl restart php8.1-fpm
sites-available:這個目錄包含 Nginx 的 virtual host 設定檔,可以放多組、不同設定的站台設定。
sites-enabled:包含指向/etc/nginx/sites-available目錄的符號連結。當sites-available中的設定檔被符號連結過來,Nginx 重啟後該站台就會生效。
網站建立好之後,切換去 grav 使用者,並在網站目錄底下開始建立 Grav:
$ su grav
$ cd ~/www
$ wget -O grav.zip https://getgrav.org/download/core/grav/latest
$ unzip grav.zip
$ rm -Rf html
$ mv grav html
$ cd ~/www/html
$ bin/grav clear
Clearing cache
Cleared: /home/grav/www/html/cache/twig/*
Cleared: /home/grav/www/html/cache/doctrine/*
Cleared: /home/grav/www/html/cache/compiled/*
Cleared: /home/grav/www/html/images/*
Cleared: /home/grav/www/html/assets/*
Touched: /home/grav/www/html/user/config/system.yaml
重啟之後,拜訪 Linode 上面給你的 public IP,有看到畫面就成功了。
References
- https://learn.getgrav.org/17/webservers-hosting/vps/linode
- https://www.laruence.com/2010/05/20/1495.html
- https://taobig.org/?p=650
🚀 Grav 2.0 補充更新(2026/08)
這篇原文是 Grav 1.7 時代的 setup。Grav 在 2026 年 5 月正式推出 2.0 stable,跟 1.7 比起來有幾個會直接影響這份 setup 的差別,整理在下面。
1. PHP 版本需求升級
| 版本 | 支援 PHP |
|---|---|
| Grav 1.7(這篇) | PHP 8.0 - 8.3 |
| Grav 2.0 | PHP 8.3 - 8.5+ |
這篇裝的是 PHP 8.1,跑 2.0 會直接掛掉。新的 setup 請從 PHP 8.3 開始:
$ sudo apt install php8.3-fpm php8.3-cli php8.3-gd php8.3-curl \
php8.3-mbstring php8.3-xml php8.3-zip php8.3-apcu
PHP-FPM pool 設定相應改成 php8.3-fpm.sock,Nginx 的 fastcgi_pass 也要記得改。
2. 升級方式完全不一樣:Migration ≠ Upgrade
沒有
bin/gpm self-upgrade從 1.7 升到 2.0。
官方原文很直接:「You do not upgrade to Grav 2.0 in place. You migrate.」原因是底層 Symfony / Twig / library stack 全部換過,原本那種「就地 mutate running site」的升級方式在實戰上一直踩雷。
正確做法:
- 在現有的 1.7 站裝一個
Migrate to Grav 2.0plugin - plugin 會在旁邊的子資料夾 stage 一個全新的 2.0 安裝
- 跑 migration — 內容、帳號、相容 plugin 都會被搬過去
- 你可以對著真實內容測試任意久
- 確定沒問題再 promote(會自動備份舊站)
- 不滿意就 reset,舊站原封不動
不要直接抓 2.0 zip 蓋到舊站 — Symfony / Twig 底層都換了,覆蓋會壞掉。
💡 還沒準備好?Grav 1.7.53 是最後一個 1.7,會持續收到 security fix,可以安心停在這裡。
3. Admin 換成 Admin Next
原本那個 jQuery + Twig 拼出來的 admin 叫 Admin Classic,2.0 預設換成 Admin Next:
- 全 API 驅動,dark / light mode 都有
- 三種頁面檢視(含 Mac Finder 風格欄位檢視)
- 自訂 dashboard、即時多人協作編輯
- 多語系 + RTL 開箱即用
Admin Classic 在 2.0 還是可以裝回去,但官方主推 Next。
4. First-party REST API(跟你直接有關)
你寫的 binapi plugin 用的就是這個 API — 只是 1.7 時代它是外掛,2.0 變成核心功能,authentication、permission check 都內建。涵蓋 pages / media / config / users / plugins / system tasks。
→ 你現有的 binapi 在 2.0 應該可以直接用,但需要加一個 compatibility flag(見下面第 8 點)。
5. Markdown 升級:GitHub Flavored Markdown 預設開啟
- ✅ Task list(
- [ ]/- [x]) - ✅ Highlight(
==重點==) - ✅ Autolink(裸 URL 自動變連結)
- ✅ 表格增強(optional plugin)
- ✅ 速度更快的新 renderer
6. ⚠️ 安全改動:Twig in Content 預設關閉
2.0 把「在 page content 裡寫 Twig」改成 sandboxed + 預設關閉(1.7 是開放的)。這是補一個真的安全漏洞。
→ 如果你的文章或 plugin 有靠 in-content Twig 渲染東西(例如 {{ site.title }}),2.0 上不會自動跑,要明確開 + 進 sandbox 才能用。
7. Native AI 整合(MCP Server)
2.0 內建 first-party MCP server,AI agent 可以透過同一套 API + 同一套權限,做你在 admin 能做的任何事。Andy Miller(Grav 創辦人)說他自己已經這樣跑站好幾個月。
未來如果想接 AI 編輯流程(例如用 Claude Code 直接改 dbyellow.com),可以走這個介面,不用自己寫整合。
8. Plugin 相容性
好消息:大部分 plugin 在 2.0 直接能用,event system / blueprint / plugin architecture 沒變。
唯一要做的:在 plugin 的 blueprint YAML 裡加一行:
compatibility:
grav2: true
讓 GPM 和 migration tool 知道你有測過 2.0。建議你幫 binapi 也補上這個 flag。
9. 我這篇文章會不會壞掉?
不會 — 內容是純 markdown 檔,跟版本無關。/user/pages/ 整個資料夾被 migration tool 原封不動搬過去,你的文章、taxonomy、frontmatter 都會跟著走。
真正會壞的是:PHP 版本、底層 Symfony/Twig、相依套件。這幾個都是 server 層的事。
10. 給「現在該不該升」的建議
| 情境 | 建議 |
|---|---|
| 站還在跑 1.7.53,沒特殊需求 | 留在 1.7.53,繼續收 security fix |
| 想用 Admin Next / 想接 AI agent | 排時間用 Migrate plugin 升 2.0 |
| 自己有 plugin | 先測試在 2.0 跑得起來,順便加 compatibility: { grav2: true } |
| 純架站教學用途 | 可以直接從 2.0 開始學,這篇的 nginx + php-fpm 概念還是通用的 |
References(2.0 相關)
- https://getgrav.org/blog/grav-2-stable-released
- https://learn.getgrav.org/16/migration/migrating-from-grav-1
- https://learn.getgrav.org/16/advanced/api-reference
- https://getgrav.org/blog/admin-next-a-modern-admin-experience
- https://getgrav.org/blog/grav-2-developer-guide-compatibility-flags