Hướng dẫn cài đặt FreshRSS trên Ubuntu VPS chi tiết
Cài đặt FreshRSS trên Ubuntu 24.04 với Apache, PHP 8.3 và MariaDB. Hướng dẫn cấu hình virtual host, thiết lập cron job tự động và khắc phục lỗi API cho ứng dụng di động.
Bạn đang xây dựng gì
RSS reader tự host là một feed reader chạy trên máy chủ do bạn sở hữu, nên không ai có thể tắt nó hoặc thay đổi nội dung hiển thị cho bạn. Hướng dẫn này cài FreshRSS trên một Ubuntu 24.04 VPS: Apache ở phía trước, PHP ở phía sau, MariaDB dùng để lưu trữ và một cron job để lấy bài viết mới. RSS (really simple syndication) là định dạng file mà một website publish để phần mềm có thể đọc các bài viết của website đó. FreshRSS là ứng dụng PHP thu thập các file này, lưu các bài viết và cung cấp web interface cùng một API (application programming interface) mà các ứng dụng điện thoại sử dụng.
Quy trình cài đặt khá gọn: giải nén một release, tạo database, viết một virtual host, chạy command line installer, rồi tạo account bằng cli/create-user.php. Phần lớn công việc bên dưới là những chỗ người dùng thường cấu hình sai sau đó, gồm refresh job, encoded slash cho mobile API và quyền sở hữu file.
FreshRSS 1.30.4 được publish vào ngày 21 tháng 10 năm 2026, là release hiện tại tính đến ngày 29 tháng 10 năm 2026 và cần PHP 8.1 trở lên. Ubuntu 24.04 ships PHP 8.3, nên các package của distribution là đủ và bạn không cần PHP repository của bên thứ ba.
Bắt đầu từ một LAMP stack đang hoạt động
FreshRSS là một ứng dụng PHP thông thường, vì vậy nó cần nền tảng giống như bất kỳ ứng dụng nào khác. Nếu bạn chưa xây dựng nền tảng đó, hãy làm theo hướng dẫn thiết lập LAMP stack trên Ubuntu 24.04 trước rồi quay lại đây. Tóm tắt ngắn gọn:
sudo apt update
sudo apt install -y apache2 mariadb-server php libapache2-mod-php
sudo systemctl enable --now apache2 mariadbsystemctl status apache2 sẽ báo active (running). Nếu Apache không khởi động được, nguyên nhân thường là do một tiến trình khác đang chiếm dụng port 80, và sudo ss -ltnp | grep :80 sẽ chỉ ra tiến trình đó.
Các PHP extension mà FreshRSS yêu cầu
FreshRSS coi libxml, cURL, JSON, PDO_MySQL, PCRE và ctype là bắt buộc. Nó cũng yêu cầu mbstring, iconv, Zlib, ZipArchive và GMP trên hệ thống 32 bit. Trên Ubuntu, các thành phần này được cung cấp dưới dạng các gói phân phối:
sudo apt install -y php-curl php-mbstring php-xml php-zip php-mysql php-intl php-gmp
sudo systemctl restart apache2Kiểm tra những gì PHP đã thực sự tải bằng php -m. Một extension bị thiếu sẽ không ngăn trình cài đặt khởi chạy. Nó sẽ dừng lại ở màn hình yêu cầu với một dòng màu đỏ nêu tên extension đó, đây là nơi gây khó hiểu để phát hiện vấn đề, vì vậy hãy xác nhận ngay bây giờ. Việc khởi động lại Apache rất quan trọng vì libapache2-mod-php giữ PHP bên trong tiến trình Apache, do đó một extension mới cài đặt sẽ không hiển thị với web server cho đến khi nó được khởi động lại.
Tải bản phát hành
Cài đặt FreshRSS bên ngoài web root mặc định và trỏ Apache đến đó. Giữ thư mục ứng dụng tách biệt với document root để chỉ thư mục public có thể được truy cập qua HTTP.
cd /tmp
curl -fsSLO https://github.com/FreshRSS/FreshRSS/archive/refs/tags/1.30.4.tar.gz
tar xzf 1.30.4.tar.gz
sudo mv FreshRSS-1.30.4 /srv/freshrssTiếp theo là quyền truy cập. Tài liệu FreshRSS yêu cầu chặt chẽ: user của web server sở hữu toàn bộ cây thư mục, group có quyền đọc mọi file và có quyền ghi vào ./data/.
sudo chown -R www-data:www-data /srv/freshrss
sudo chmod -R g+r /srv/freshrss
sudo chmod -R g+w /srv/freshrss/dataNếu bỏ qua bước này, trình cài đặt sẽ lỗi khi ghi cấu hình vì PHP chạy với user www-data, còn www-data không thể ghi vào thư mục do root sở hữu.
Tạo database
FreshRSS hỗ trợ SQLite, MariaDB, MySQL và PostgreSQL. SQLite cần extension php-sqlite3 nhưng không cần thiết lập database server, đồng thời lưu dữ liệu của từng user trong một file data/users/_user_/db.sqlite riêng. Thêm user không khiến dữ liệu ghi được đưa vào một file SQLite dùng chung; hướng dẫn này dùng MariaDB trong LAMP stack.
sudo mariadb -e "CREATE DATABASE freshrss CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mariadb -e "CREATE USER 'freshrss'@'localhost' IDENTIFIED BY 'ReplaceThisPassword';"
sudo mariadb -e "GRANT ALL PRIVILEGES ON freshrss.* TO 'freshrss'@'localhost';"
sudo mariadb -e "FLUSH PRIVILEGES;"Dùng utf8mb4, không dùng utf8. Feed có thể chứa emoji và các bộ chữ không phải Latin, trong khi encoding utf8 cũ với độ dài 3 byte không thể lưu ký tự 4 byte. Ký tự không được hỗ trợ sẽ gây lỗi khi MariaDB chạy ở strict mode mặc định; việc cắt ngắn dữ liệu phụ thuộc vào SQL mode và các tùy chọn của câu lệnh như IGNORE.
Apache virtual host
Thư mục public là p/, không phải là thư mục gốc của cây thư mục. Mọi thứ khác, bao gồm cả tệp cấu hình chứa mật khẩu cơ sở dữ liệu của bạn, đều nằm phía trên document root nơi Apache sẽ không bao giờ phục vụ nội dung.
<VirtualHost *:80>
ServerName rss.example.com
DocumentRoot /srv/freshrss/p/
<Directory /srv/freshrss/p>
AllowOverride AuthConfig FileInfo Indexes Limit
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
AllowEncodedSlashes On
</VirtualHost>Lưu tệp đó dưới tên /etc/apache2/sites-available/freshrss.conf và kích hoạt nó:
sudo a2enmod rewrite
sudo a2ensite freshrss
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2configtest sẽ in ra Syntax OK. AllowEncodedSlashes On trông có vẻ tùy chọn nhưng thực tế không phải vậy: Google Reader API gửi các định danh feed chứa %2F, và nếu không có chỉ thị này, Apache sẽ từ chối chúng, khiến các ứng dụng di động không thể đồng bộ trong khi giao diện web vẫn hoạt động bình thường.
Thêm HTTPS trước khi đăng nhập
Bạn sắp nhập mật khẩu vào trang web này, vì vậy hãy lấy chứng chỉ trước. Trỏ một bản ghi A về server, sau đó làm theo hướng dẫn thiết lập Certbot cho Apache trên Ubuntu và chạy sudo certbot --apache -d rss.example.com. Certbot sẽ viết lại virtual host cho cổng 443 và thêm lệnh chuyển hướng. Xác nhận bằng curl -I https://rss.example.com/, lệnh này sẽ trả về 200 hoặc chuyển hướng đến trang đăng nhập.
Chạy trình cài đặt từ dòng lệnh
FreshRSS có trình cài đặt trên trình duyệt, nhưng phiên bản dòng lệnh có thể lặp lại và lưu lại hồ sơ chính xác về những gì bạn đã chọn.
sudo -u www-data php /srv/freshrss/cli/do-install.php \
--default-user admin --auth-type form --environment production \
--base-url https://rss.example.com --language en --api-enabled \
--db-type mysql --db-host localhost --db-user freshrss \
--db-password 'ReplaceThisPassword' --db-base freshrss
sudo -u www-data php /srv/freshrss/cli/create-user.php \
--user admin --password 'a-long-passphrase' --api-password 'a-different-passphrase'Chạy cả hai lệnh dưới quyền www-data. Chạy chúng dưới quyền root sẽ tạo ra các tệp cấu hình thuộc sở hữu của root, khiến giao diện web không thể lưu bất kỳ cài đặt nào sau đó. --environment production cũng rất quan trọng, vì cài đặt development sẽ in các thông báo PHP lên trang web.
Tải https://rss.example.com/ và đăng nhập với tư cách admin.
Vì sao feed không tự làm mới
Không có tiến trình nào polling feed cho đến khi bạn cấu hình. Việc mở FreshRSS không lên lịch lấy feed; nếu không có scheduler, hãy dùng liên kết cập nhật trong menu điều hướng để lấy bài viết thủ công. Cách khắc phục là dùng script mà project cung cấp cho việc này, app/actualize_script.php, chạy từ cron.
sudo crontab -u www-data -eThêm một dòng:
*/20 * * * * php /srv/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&1Hai mươi phút là lịch chạy hợp lý, nhưng khoảng thời gian làm mới của từng feed vẫn được áp dụng: mặc định là một giờ, và từng feed hỗ trợ khoảng thời gian tối thiểu 15 phút. Trước tiên, hãy chạy lệnh này thủ công một lần:
sudo -u www-data php /srv/freshrss/app/actualize_script.phpOutput bình thường sẽ báo lần chạy làm mới và kết thúc mà không có lỗi PHP; tên feed sẽ xuất hiện khi có bài viết được thêm hoặc cập nhật. Nếu hoàn toàn không có output, hãy kiểm tra error output của PHP và log FreshRSS; việc không có output tự nó không xác định cron user là nguyên nhân. Lỗi permission trên data/ cần được kiểm tra ownership và quyền ghi.
Thêm các feed đầu tiên của bạn
Sử dụng nút dấu cộng ở phía trên bên trái giao diện, dán địa chỉ trang web vào, và FreshRSS sẽ tự động tìm liên kết feed cho bạn. Hầu hết các trang web vẫn xuất bản feed ngay cả khi họ không quảng bá nó, thường là tại /feed, /rss hoặc /atom.xml. Các danh mục đóng vai trò như thư mục, và việc di chuyển một feed giữa các danh mục này chỉ cần thao tác kéo thả.
Nếu bạn chuyển từ một trình đọc khác, hãy xuất tệp OPML từ đó và nhập vào trang quản lý đăng ký. OPML (outline processor markup language) là định dạng danh sách feed tiêu chuẩn, và mọi trình đọc đáng dùng đều hỗ trợ nó. Một lần nhập dữ liệu lớn sẽ chạy chậm trong lần làm mới đầu tiên vì mỗi feed đều được tải về một lần, vì vậy hãy để cron chạy xong trước khi bạn đánh giá tốc độ.
Đọc trên điện thoại
FreshRSS hỗ trợ Google Reader API, được nhiều RSS app hỗ trợ. Hai điều sau phải đúng. Trong phần cài đặt xác thực, "Allow API access" phải được bật; flag --api-enabled ở trên đã bật tùy chọn này. Trong profile của bạn, trường mật khẩu API phải có giá trị. Mật khẩu này được tách riêng khỏi mật khẩu đăng nhập vì điện thoại là thiết bị dễ bị mất hơn.
Truy cập https://rss.example.com/api/ để tự động chạy các kiểm tra cấu hình server. Cấu hình hoạt động sẽ trả về PASS. Nếu kiểm tra thất bại, hãy kiểm tra cài đặt AllowEncodedSlashes On của virtual host và lỗi được báo cáo. Trong app, nhập địa chỉ server là https://rss.example.com/api/greader.php, username là user FreshRSS của bạn, còn password là mật khẩu API.
Giải pháp thay thế Docker
Nếu bạn không muốn tự tay bảo trì PHP và Apache, dự án có phát hành một image freshrss/freshrss chính thức, và một file compose sẽ cung cấp cho bạn ứng dụng cùng cơ sở dữ liệu đi kèm. Sự đánh đổi ở đây là điều thường thấy: ít thành phần chạy trực tiếp trên host hơn, nhưng thêm một lớp cần debug khi có sự cố, và bạn vẫn cần một reverse proxy để xử lý TLS (transport layer security). Nếu cách này phù hợp với bạn hơn, các kiến thức cơ bản về Docker Compose cho VPS sẽ bao gồm định dạng file, và dòng cron sẽ trở thành docker exec --user www-data freshrss php ./app/actualize_script.php.
Sao lưu và nâng cấp
Trạng thái của bạn nằm ở 2 thứ: database và /srv/freshrss/data/. Dùng sudo mysqldump freshrss > freshrss.sql để dump database, sao chép /srv/freshrss/data/, rồi lưu cả 2 ở một nơi khác, không phải trên server này. Danh sách subscription cũng nên được export sang OPML định kỳ, vì file đó có thể chuyển danh sách feed sang các reader khác hỗ trợ OPML. File này không lưu các thiết lập riêng của FreshRSS như tần suất refresh, credential, user agent hoặc rule web scraping bằng XPath; hãy dùng SQLite export để giữ cả những thiết lập đó.
Nâng cấp là giải nén một release mới hơn vào cùng thư mục rồi chạy lại các lệnh ownership. Làm theo lộ trình nâng cấp từng version của project và kiểm tra FreshRSS sau mỗi bước. FreshRSS sẽ tự chạy database migration khi tải trang tiếp theo. Hãy backup trước, vì migration thất bại trên một database mà bạn không thể restore sẽ khiến hệ thống không thể khôi phục. Nếu bạn cài từ tarball cũ như 1.29.1, hãy nâng lên 1.30.4: các security release thuộc nhánh 1.30.x bổ sung việc xoay vòng session ID khi thay đổi authentication và giới hạn cho ZIP import; 1.30.4 cũng cập nhật các Alpine Docker image thay thế để xử lý các lỗ hổng PHP nghiêm trọng. Reader là một service có rủi ro thấp và là ứng viên phù hợp để chạy trước nếu bạn đang xem xét danh sách những thứ đáng tự host.
FAQ
Vì sao các feed của tôi chỉ cập nhật khi tôi mở FreshRSS?
Việc mở trang không lập lịch lấy feed. Nếu không có scheduler, hãy dùng liên kết cập nhật để refresh thủ công. Thêm dòng cron gọi app/actualize_script.php với user www-data, sau đó chạy script thủ công một lần và đọc output của nó. Nếu không có output, hãy kiểm tra error output của PHP và log FreshRSS, rồi xác nhận user chạy cron có quyền ghi vào data/.
Vì sao ứng dụng mobile của tôi không kết nối được nhưng website vẫn hoạt động?
Google Reader API đặt các slash đã encode (%2F) trong request path, và Apache mặc định từ chối chúng. Thêm AllowEncodedSlashes On vào virtual host rồi reload Apache. Xác nhận cách sửa bằng cách mở https://rss.example.com/api/. Kiểm tra cấu hình tự động tại đó phải báo PASS. Đồng thời kiểm tra API password đã được đặt trong profile, vì password này tách biệt với password đăng nhập.
Tôi nên dùng SQLite hay MariaDB?
SQLite cần extension php-sqlite3, nhưng không cần quản lý database server hoặc database password. Mỗi user có một file database SQLite riêng. Vì vậy, thêm user thứ hai không gây contention trên một file dùng chung và không cần MariaDB. Dùng MariaDB nếu bạn muốn sử dụng database server trong LAMP stack. Sau này vẫn có thể chuyển đổi giữa hai loại thông qua các lệnh export và import, nên đây không phải quyết định vĩnh viễn.
Installer bị lỗi khi ghi cấu hình. Nguyên nhân là gì?
PHP chạy dưới user www-data trong Apache, và user đó không thể ghi vào directory thuộc sở hữu của root. Chạy lại sudo chown -R www-data:www-data /srv/freshrss và sudo chmod -R g+w /srv/freshrss/data, sau đó khởi động installer lần nữa. Nếu bạn đã chạy command-line installer bằng root, hãy sửa ownership của các file mà installer tạo trong data/ trước khi thử lại.
RSS reader tự host cần server mạnh đến mức nào?
Rất ít. FreshRSS được thiết kế để chạy trên phần cứng khiêm tốn, nhưng mức sử dụng tài nguyên phụ thuộc vào các feed, kích thước bài viết và thiết lập retention của bạn. Dung lượng disk tăng theo retention. Vì vậy, hãy đặt chính sách purge bài viết trong phần thiết lập archiving thay vì giữ mọi thứ vô thời hạn.