Hero Image
- Mark

使用 Grav 快速架設 Blog

我最近因為想要練習寫文章,所以找了幾個部落格內容管理系統,最後選定使用 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. 在現有的 1.7 站裝一個 Migrate to Grav 2.0 plugin
  2. plugin 會在旁邊的子資料夾 stage 一個全新的 2.0 安裝
  3. 跑 migration — 內容、帳號、相容 plugin 都會被搬過去
  4. 你可以對著真實內容測試任意久
  5. 確定沒問題再 promote(會自動備份舊站)
  6. 不滿意就 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

Other Related Posts:

Classic Modern Architecture

今天試裝之前為了研究 linux 系統 而有小玩的 windows wsl 系統,這次嘗試,是因為在公司mac 上玩了 alacritty 這個新的終端工具,想基於他的跨平台性能,iterm 2 以下安裝為非乾淨的 windows 安裝,有一些指令,可能會稍微有誤差

使用管理員執行 Powershell

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

安裝 Ubuntu 18.04 url

sudo apt-get update
sudo apt-get...
18th Sep 2020