Dùng Synology để host một cái blog không phải WordPress

Con NAS ở góc phòng đã chạy sẵn hai mươi bốn trên hai mươi bốn rồi. Đây là câu chuyện dọn cái blog về ở trong đó.

Con NAS của tôi nằm ở góc nhà, chạy suốt hai năm nay không nghỉ ngày nào. Nó giữ ảnh, giữ tài liệu, giữ mấy bản sao lưu mà tôi hy vọng cả đời không phải mở tới. Mỗi tháng nó ăn chừng hai mươi lăm ngàn tiền điện. Nó là một cái máy tính hoàn chỉnh, bật hai mươi bốn trên hai mươi bốn, và suốt hai năm đó tôi chỉ dùng nó làm đúng một việc: chứa file.

Trong khi đó cái blog của tôi nằm ở một server thuê bên Mỹ.

Nghĩ lại thì hơi buồn cười. Tôi đi thuê một cái máy ở nửa vòng trái đất để chạy một thứ nhẹ hơn cả cái app xem ảnh trên chính con máy đang chạy trong nhà mình.

Vài bài này là hướng dẫn dựng Quire Ink lên NAS Synology. Có phần lý thuyết, có phần thao tác, và có một cái lỗi mà chính tôi mới gặp phải tuần này, phải sửa xong mới viết bài blog này.

Quire Ink là cái gì, kể lại từ đầu

Hồi tháng sáu tôi viết một bài dài về chuyện tự cất nhà sau hai mươi năm ở trọ. Đại ý: tôi viết blog từ thời Xanga, đi qua Yahoo 360, Yobanbe, Tumblr, WordPress, và lần nào cũng mất nhà. Nên tôi tự làm lấy một cái nền tảng blog cho riêng mình.

Nếu bạn có đọc bài đó thì xin đọc lại phần kỹ thuật với con mắt hoài nghi, vì cái nó mô tả gần như không còn tồn tại. Hồi đó Quire chạy trên Next.js, dựa vào Vercel, dùng PostgreSQL, đăng nhập qua Google. Bản 2.0 đã đập đi xây lại từ móng. Bây giờ nó tên là Quire Ink, và ruột gan là thế này:

Một tiến trình duy nhất. Hai file SQLite. Hết.

Không có database server phải nuôi. Không có Redis. Không có hàng chờ, không có worker, không có cái gì cần cài bên cạnh nó. Bạn chạy một lệnh, nó lắng nghe một port, và đó là cả cái blog. Chính vì gọn tới mức đó mà nó vừa vặn với một con NAS yếu òm, thứ vốn không được sinh ra để chạy web.

Còn mấy thứ đáng nói khác thì vẫn còn nguyên và tốt hơn: sáu bảng màu sáng tối, bốn font đọc, chế độ đọc sách chia hai cột như trang giấy, bút dạ quang năm màu để tô chữ, tìm kiếm hiện kết quả ngay khi gõ, bản tin gửi email, analytic không dùng cookie. Một trang bài viết nặng khoảng một trăm mười bốn KB, nhẹ hơn một tấm ảnh chụp bằng điện thoại vài chục lần. Và cái MCP server vẫn ở đó, nghĩa là tôi vẫn viết blog bằng điện thoại trên OneNote và nói chuyện với Claude rồi bảo nó đăng bài giùm.

Một chỗ tôi phải đính chính cho rõ, vì bài cũ nói sai: giấy phép không còn là MIT. Bây giờ là PolyForm Noncommercial, kèm một cho phép riêng để ai muốn chạy bản phát hành nguyên gốc vào việc kinh doanh, kể cả bán hosting, thì cứ chạy. Chỉ bản đã sửa đổi mà đem đi kinh doanh mới phải hỏi. Blog cá nhân thì miễn phí hoàn toàn, không có ngoại lệ nào.

Anh muốn xem tận mắt trước khi cài thì có bản demo ở demo.quireink.com, không cần đăng ký gì cả.

NAS có phải chỗ tốt cho một cái blog hay không?

Câu trả lời thật thà là: tùy anh viết cho ai.

Cái được thì rõ. Máy đã chạy sẵn rồi, thêm một cái blog gần như không tốn thêm điện. Dữ liệu nằm trong nhà, trong tay mình, và nằm chung chỗ với cái hệ thống sao lưu của NAS. Không có hóa đơn hàng tháng. Không có công ty nào có thể đổi chính sách hay quản lý nội dung của ta.

Cái phải mất thì cũng phải nói cho sòng phẳng. Đường truyền ở nhà không phải đường truyền của trung tâm dữ liệu. Cúp điện là blog sập. Nhà mạng bảo trì là blog sập. Tốc độ tải lên ở nhà thường chỉ bằng một phần mấy tốc độ tải xuống, mà blog thì toàn tải lên. Và nhiều nhà mạng Việt Nam chặn cổng 80 với 443 trên gói cư dân, nghĩa là anh phải xin mở, phải đổi gói, hoặc phải đi đường vòng.

Nên chúng ta phải đồng thuận: nếu blog của anh là chỗ kiếm cơm, có khách hàng ghé đọc, thì thuê một con VPS mười lăm đô một năm vẫn là lựa chọn đúng. Còn nếu nó là chỗ anh viết cho mình, cho vài chục người quen, mà anh thích cái cảm giác biết chính xác cái máy đang giữ chữ của mình nằm ở đâu trong nhà, thì NAS là chỗ tốt. Tôi thuộc nhóm thứ hai, ít nhất là với những thứ tôi không cần cả thế giới đọc, chưa kể là với trang manhhung.me này tôi còn dùng cả Cloudflare R2 để làm backup khi Cloudflare Tunnel không reach được tới NAS, nhưng đó là một câu chuyện khác, tôi sẽ biến nó thành tính năng trong các phiên bản sau của QuireInk.

Vì sao không phải WordPress

Nói cho công bằng: WordPress cài lên Synology được, và Container Manager có sẵn mẫu để làm chuyện đó. Nhiều người đã làm rồi. Tôi cũng đang nuôi hơn chục cái site WordPress nặng cho công việc, nên tôi không có định kiến gì với nó.

Nhưng anh thử đếm số thứ mà anh phải dựng lên. WordPress cần một database MySQL hoặc MariaDB, tức là thêm một container. Cần PHP-FPM. Cần một web server đứng trước. Cần một tầng cache, vì WordPress trần thì chậm và nặng nề. Bốn thứ đó phải nói chuyện được với nhau, phải cùng bật cùng tắt, và mỗi thứ có phiên bản riêng, lịch cập nhật riêng, cách lỗi riêng.

Trên một con server AWS với 32 nhân thì đó là chuyện nhỏ. Trên một con NAS hai nhân đang bận nén ảnh và chạy sao lưu ban đêm thì nó là một cái gánh nặng thực sự.

Rồi tới phần bảo trì. Mỗi plugin là một cái vấn đề. Tôi đã đọc đủ nhiều thông báo lỗ hổng plugin WordPress để biết rằng câu hỏi không phải là có bị gì không, mà là bao giờ nó có vấn đề. Mà cái máy này không phải là một con server vô danh ngoài kia: nó nằm trong mạng nhà tôi, cạnh ảnh gia đình và tài liệu công việc. Một cái blog bị chiếm quyền trên một con VPS là mất một cái blog. Trên NAS, nó là cả một vấn đề dù rằng khả năng xảy ra là rất thấp.

Chế độ đọc như trang sách của QuireInk là cái tôi rất thích.
Chế độ đọc như trang sách của QuireInk là cái tôi rất thích.
Quire Ink thì không có plugin để cài, không có database server để nuôi, không có PHP. Một tiến trình, hai file. Bề mặt tấn công nhỏ hơn không phải vì nó đơn giản và có ít thứ hơn để mà hư hỏng.

Đổi lại, anh mất cái mà WordPress mạnh nhất: hai mươi năm plugin cho mọi nhu cầu trên đời. Muốn bán hàng, muốn form phức tạp, muốn nhiều tác giả phân quyền, muốn một cái gì đó lạ mà chắc chắn có người viết sẵn plugin, thì cứ WordPress. Quire Ink cố tình là blog một chủ. Nếu anh cần nhiều hơn một cái blog một người xài, nó không dành cho anh, và tôi thấy bản thân phù hợp với việc đó.

Trước khi bắt đầu

Máy của anh có chạy được container không. Đây là chỗ dễ hụt nhất. Phần lớn NAS Synology dùng chip Intel hoặc AMD thì chạy được. Mấy dòng dùng chip ARM, thường là các model đuôi j hoặc các model giá rẻ, thì gói Container Manager không cài được, và không có cách đi đường vòng nào tử tế cả. Anh vào Package Center gõ tìm Container Manager, thấy nó thì chạy được, không thấy thì thôi.

Trên DSM 7.2 trở lên nó tên là Container Manager. DSM cũ hơn thì tên là Docker. Cùng một thứ.

Chỗ để dữ liệu. Mở File Station, vào thư mục docker (nếu chưa có thì cài Container Manager xong nó tự tạo), tạo bên trong một thư mục tên quireink, rồi tạo tiếp hai thư mục con datauploads.

Đường dẫn đầy đủ sẽ là:

/volume1/docker/quireink/data
/volume1/docker/quireink/uploads

Nếu NAS của anh có nhiều ổ thì có thể là volume2, volume3. Nhớ số đó, lát nữa cần.

Cái lỗi nhỏ mà tôi vừa dẫm phải trên Synology

Đây là phần tôi muốn kể kỹ, vì nó vừa xảy ra tuần này và nó giải thích một dòng cấu hình mà nếu không hiểu thì anh sẽ chép vẹt lại mà không biết để làm gì, điều này quen thuộc với những ai quen xài docker trên Synology, nhưng tôi muốn nhắc lại cho những ai chưa biết. Container chạy dưới một danh tính người dùng. Quire Ink chạy dưới người dùng số 1000. Còn cái thư mục anh vừa tạo bằng File Station thì thuộc về một người dùng khác, thường là root hoặc tài khoản DSM của anh.

Kết quả: container bật lên, cố mở file database, không có quyền ghi, và chết. Cái đáng nói không phải là nó chết, mà là cách nó chết. Không có dòng nào nói "thư mục này tôi không ghi được". Cái nó nhả ra là một vệt lỗi SQLite:

SQLiteError: unable to open database file

Anh cầm dòng đó lên Google, anh sẽ đọc về ổ đầy, về file hỏng, về đủ thứ chuyện không liên quan. Còn nguyên nhân thật thì nằm ở quyền truy cập thư mục, cách đó ba tầng. Tuần này tôi ngồi dựng thử đúng cảnh đó trên một máy Linux, thấy nó chết, rồi sửa. Bây giờ image nhận hai biến PUIDPGID. Anh đưa cho nó số hiệu người dùng và nhóm sở hữu thư mục, nó tự nhận quyền ở lần khởi động đầu tiên rồi hạ quyền xuống trước khi chạy, nên phần mềm không bao giờ chạy bằng quyền quản trị. Lần khởi động sau nó chỉ liếc một cái rồi bỏ qua, vì đi sửa lại quyền cả cây thư mục ảnh mỗi lần khởi động lại thì một cú restart sẽ thành một sự cố.

Cách tìm hai con số đó. Bật SSH trong Control Panel, mục Terminal & SNMP, rồi từ máy tính gõ:

ssh tenban@ip-cua-nas
id

Nó trả về đại loại uid=1026(hung) gid=100(users). Hai số đó là PUIDPGID của anh. Trên phần lớn NAS Synology, tài khoản đầu tiên là 1026 và nhóm users là 100, nhưng đừng đoán, cứ gõ cho chắc.

Dựng bằng Project

Container Manager có mục Project, chỗ bạn dán thẳng một file compose vào thay vì bấm qua một đống bước. Tạo project mới, đặt tên quireink, chọn đường dẫn /volume1/docker/quireink, chọn tạo file mới, rồi dán cái này vào:

services:
  quire:
    image: quireink/quireink:2.1
    container_name: quireink
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      SITE_URL: https://blog.tenmiencuaban.com
      PUID: 1026
      PGID: 100
    volumes:
      - /volume1/docker/quireink/data:/var/lib/quire/data
      - /volume1/docker/quireink/uploads:/var/lib/quire/uploads

Sửa ba chỗ: tên miền, hai con số vừa lấy được, và đường dẫn volume nếu NAS của anh không phải volume1. Vài chữ về mấy dòng vừa rồi, vì tôi ghét kiểu hướng dẫn bảo chép mà không nói tại sao.

image: quireink/quireink:2.1 là nhãn hai số. Nó nhận các bản vá lỗi trong dòng 2.1 nhưng không tự nhảy sang bản có tính năng mới. Đây là nhãn nên ghim. Nhãn latest để nghịch thì được, để chạy blog thật thì đừng, vì nhiều tính năng mới đôi khi tôi làm còn lỗi.

SITE_URL phải là địa chỉ công khai thật, có https://, không có dấu gạch chéo ở cuối. Nguồn RSS, ảnh chia sẻ lên Facebook, link xem thử bài nháp, tất cả dựng từ chuỗi này. Điền sai thì mọi thứ vẫn chạy, chỉ có các đường dẫn là trỏ sai, mà loại hỏng đó thì vài tuần sau mới phát hiện.

Hai dòng volumes là chỗ chữ của anh sống. Quên nó đi thì blog vẫn chạy ngon lành, cho tới lần đầu anh cập nhật container, và mọi thứ biến mất cùng lúc.

Bấm Build, chờ nó kéo image về. Xong thì mở trình duyệt vào http://ip-cua-nas:3000, thấy trang chủ trống trơn của một cái blog mới tinh là được.

Tài khoản đầu tiên

Blog đã chạy nhưng chưa có ai làm chủ nó. Trong Container Manager, vào Container, chọn quireink, mở tab Terminal, tạo một phiên bash rồi gõ:

bun run user create --username tenban --email email@cuaban.com

Nó sẽ hỏi mật khẩu, tối thiểu mười hai ký tự.

Nếu anh thích làm qua SSH hơn thì nhớ cái này, tôi vừa thử và vừa dính: phải có cờ -it.

sudo docker exec -it quireink bun run user create --username tenban --email email@cuaban.com

Thiếu -it thì nó không có chỗ nhận mật khẩu anh gõ, và nó báo Too short. Minimum 12 characters. dù anh chưa kịp gõ chữ nào. Một thông báo lỗi nói đúng sự thật mà lại dẫn người ta đi sai đường.

Đăng nhập lần đầu ở /admin, nó sẽ bắt anh cài xác thực hai lớp ngay tại chỗ, và trang quản trị không mở được cho tới khi làm xong. Chuẩn bị sẵn app Authy hay Google Authenticator trên điện thoại.

HTTPS

Tới đây blog đã chạy nhưng còn ở http:// và một cổng số. Phần còn lại là đưa nó ra ngoài cho tử tế.

DSM có sẵn reverse proxy. Vào Control Panel, Login Portal, tab Advanced, chọn Reverse Proxy, tạo một cái mới: nguồn là blog.tenmiencuaban.com cổng 443 HTTPS, đích là localhost cổng 3000 HTTP. Rồi vào tab Certificate xin một chứng chỉ Let's Encrypt và gán nó cho cái tên miền đó.

Nghe thì hai phút. Với tôi thì nó là một buổi chiều, và tôi đã viết hẳn một bài về chuyện cái SSL lặng lẽ chết trên NAS.

Tóm tắt cho ai lười bấm: nếu anh có chặn kết nối từ nước ngoài vào NAS, thì cách xác thực mặc định của Let's Encrypt sẽ không bao giờ chạy được. Nó đòi phải gõ cửa được vào máy anh từ nhiều nơi trên thế giới cùng lúc, mà cái tường lửa anh tự tay bật lên chính là thứ chặn nó lại. Cert sẽ chết lặng lẽ, và ba tháng sau anh mới biết. Đường ra là dùng xác thực qua DNS thay vì qua HTTP. Bài kia có đủ chi tiết.

Còn nếu nhà mạng của anh chặn cổng 443 thì reverse proxy của DSM cũng chịu. Lúc đó phải đi đường Cloudflare Tunnel, mà cái đó xứng đáng một bài riêng.

Sao lưu, và một chút mỉa mai

Có một điều buồn cười khi chạy blog trên NAS: anh vừa đặt chữ của mình vào đúng cái máy mà anh vẫn dùng để sao lưu mọi thứ khác, và rồi rất dễ quên rằng bản thân nó cũng cần được sao lưu.

Quire Ink có sẵn một nút trong trang quản trị, bấm cái là nó gói cả hai database lẫn toàn bộ ảnh thành một file cho anh tải về. Bấm nút đó là việc của anh, không ai bấm thay được.

Ngoài ra vì dữ liệu nằm trong hai thư mục thật trên NAS, anh có thể bảo Hyper Backup ôm luôn thư mục /volume1/docker/quireink đi cùng với mấy thứ khác. Đây chính là lý do bài này dùng thư mục thật thay vì volume ảo của Docker: cái gì Hyper Backup nhìn thấy được thì cái đó mới được sao lưu.

Rốt cuộc thì

Cái blog bây giờ nằm trong con máy ở góc phòng, cách chỗ tôi đang ngồi viết bài này chừng bốn mét. Nó không nhanh hơn con server bên Mỹ. Nó không ổn định hơn. Nếu tôi phải chọn chỗ đặt một cái blog cho công việc, tôi vẫn chọn thuê máy.

Nhưng bài trước tôi có viết rằng suốt hai mươi năm tôi toàn đi ở trọ, và mỗi lần chủ nhà đổi ý là tôi mất một phần ký ức viết lách. Cái server tôi đang thuê, nói cho cùng, cũng là một cái nhà thuê. Rẻ, tốt, tử tế, nhưng vẫn là của người khác.

Còn con NAS này thì tôi mua đứt rồi.

Có thể đó là một sự khác biệt hoàn toàn tâm lý. Nhưng sau hai mươi năm, tôi nghĩ mình có quyền đòi một chút tâm lý.


Mã nguồn Quire Ink ở github.com/joiha-steven/quireink. Image Docker ở quireink/quireink, chạy được trên cả máy Intel lẫn ARM.