Ubuntu 24.04에 FreshRSS 설치 및 설정 가이드
Ubuntu 24.04 VPS에서 Apache, PHP, MariaDB를 사용하여 FreshRSS를 직접 구축합니다. 릴리스 압축 파일 설치부터 가상 호스트 설정, cron 자동 새로고침 구성 및 모바일 API 연동까지 단계별로 안내합니다.
구축할 내용
자체 호스팅 RSS 리더는 사용자가 소유한 서버에서 실행되는 피드 리더입니다. 따라서 누구도 이를 중단시키거나 표시되는 내용을 임의로 변경할 수 없습니다. 이 가이드에서는 Ubuntu 24.04 VPS에 FreshRSS를 설치합니다. 구성 요소는 앞단의 Apache, 뒷단의 PHP, 저장소를 위한 MariaDB, 그리고 새 기사를 가져오는 cron 작업 하나입니다. RSS(Really Simple Syndication)는 사이트가 소프트웨어에서 기사를 읽을 수 있도록 게시하는 파일 형식입니다. FreshRSS는 이러한 파일을 수집하고 기사를 보관하며, 웹 인터페이스와 모바일 앱이 통신하는 API(Application Programming Interface)를 제공하는 PHP 애플리케이션입니다.
설치 과정 자체는 간단합니다. 릴리스 압축을 풀고, 데이터베이스를 생성하고, 가상 호스트를 작성한 뒤 명령줄 설치 프로그램을 한 번 실행하면 됩니다. 아래 작업 중 대부분은 설치 후 흔히 실수하는 부분인 새로고침 작업, 모바일 API를 위한 슬래시 인코딩, 파일 소유권 설정에 집중되어 있습니다.
2026년 7월 기준 최신 릴리스는 FreshRSS 1.29.1이며, PHP 8.1 이상이 필요합니다. Ubuntu 24.04는 PHP 8.3을 기본으로 제공하므로, 배포판 패키지만으로 충분하며 별도의 타사 PHP 저장소는 필요하지 않습니다.
작동 중인 LAMP 스택에서 시작하기
FreshRSS는 일반적인 PHP 애플리케이션이므로 다른 애플리케이션과 동일한 기반이 필요합니다. 아직 해당 기반을 구축하지 않았다면 먼저 Ubuntu 24.04용 LAMP 스택 설정을 수행한 뒤 여기로 돌아오십시오. 요약하자면 다음과 같습니다.
sudo apt update
sudo apt install -y apache2 mariadb-server php libapache2-mod-php
sudo systemctl enable --now apache2 mariadbsystemctl status apache2 명령은 active (running)를 출력해야 합니다. Apache가 시작되지 않는다면, 일반적으로 다른 프로세스가 이미 80번 포트를 점유하고 있는 경우이며 sudo ss -ltnp | grep :80 명령을 통해 해당 프로세스를 확인할 수 있습니다.
FreshRSS가 요구하는 PHP 확장 모듈
FreshRSS는 libxml, cURL, JSON, PDO_MySQL, PCRE 및 ctype을 필수 항목으로 간주합니다. 또한 mbstring, iconv, Zlib, ZipArchive를 요구하며, 32비트 시스템에서는 GMP가 필요합니다. Ubuntu에서는 다음 배포판 패키지를 통해 설치할 수 있습니다.
sudo apt install -y php-curl php-mbstring php-xml php-zip php-mysql php-intl php-gmp
sudo systemctl restart apache2php -m 명령을 사용하여 PHP가 실제로 로드한 모듈을 확인하십시오. 확장 모듈이 누락되어도 설치 프로그램은 시작됩니다. 다만 요구 사항 확인 단계에서 누락된 확장 모듈을 빨간색으로 표시하며 중단되는데, 이 시점에서 문제를 발견하면 혼란스러울 수 있으므로 지금 미리 확인하십시오. Apache를 재시작하는 과정이 중요합니다. libapache2-mod-php는 PHP를 Apache 프로세스 내부에 유지하므로, 웹 서버를 재시작하기 전까지는 새로 설치한 확장 모듈을 인식하지 못하기 때문입니다.
릴리스 다운로드
FreshRSS를 기본 웹 루트 외부 경로에 설치하고 Apache가 해당 경로를 가리키도록 설정합니다. 애플리케이션 디렉터리를 문서 루트와 분리하면 HTTP를 통해 public 폴더에만 접근할 수 있습니다.
cd /tmp
curl -fsSLO https://github.com/FreshRSS/FreshRSS/archive/refs/tags/1.29.1.tar.gz
tar xzf 1.29.1.tar.gz
sudo mv FreshRSS-1.29.1 /srv/freshrss이제 FreshRSS 문서에서 엄격하게 요구하는 권한을 설정합니다. 웹 서버 사용자가 전체 트리의 소유권을 가져야 하며, 그룹은 모든 파일을 읽을 수 있어야 하고, ./data/ 디렉터리에 대해서는 쓰기 권한이 있어야 합니다.
sudo chown -R www-data:www-data /srv/freshrss
sudo chmod -R g+r /srv/freshrss
sudo chmod -R g+w /srv/freshrss/data이 과정을 건너뛰면 설치 프로그램이 설정을 기록할 때 실패합니다. PHP는 www-data 사용자로 실행되는데, www-data 사용자는 root가 소유한 디렉터리에 파일을 쓸 수 없기 때문입니다.
데이터베이스 생성
FreshRSS는 SQLite, MariaDB, MySQL 및 PostgreSQL을 지원합니다. SQLite는 별도의 설정이 필요 없으며, 한 명의 사용자가 수백 개의 피드를 관리하는 용도로 적합합니다. 여러 사용자가 인스턴스를 공유한다면 MariaDB가 더 나은 선택입니다. 피드 갱신 작업과 웹 인터페이스 간의 동시 쓰기 작업이 단일 파일 잠금을 두고 경쟁하지 않기 때문입니다.
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;"utf8 대신 utf8mb4를 사용하십시오. 피드에는 이모지와 비라틴 문자가 포함될 수 있으며, 구형 3바이트 utf8 인코딩은 4바이트 문자가 처음 나타나는 지점에서 기사 제목을 잘라버립니다.
Apache 가상 호스트
공개 디렉터리는 p/이며, 트리 최상단이 아닙니다. 데이터베이스 비밀번호가 포함된 설정 파일을 비롯한 나머지 모든 파일은 Apache가 절대 제공하지 않는 문서 루트 상위에 위치합니다.
<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>해당 내용을 /etc/apache2/sites-available/freshrss.conf으로 저장하고 활성화합니다.
sudo a2enmod rewrite
sudo a2ensite freshrss
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2configtest는 Syntax OK를 출력해야 합니다. AllowEncodedSlashes On은 선택 사항처럼 보이지만 필수입니다. Google Reader API는 %2F을 포함하는 피드 식별자를 전송하는데, 이 지시어가 없으면 Apache가 이를 거부합니다. 결과적으로 웹 인터페이스는 정상 작동하지만 모바일 앱의 동기화는 실패하게 됩니다.
로그인 전 HTTPS 적용
이 사이트에 비밀번호를 입력하기 전이므로 먼저 인증서를 발급받아야 합니다. 서버에 A 레코드를 지정한 뒤, Ubuntu의 Apache용 Certbot 설정을 따라 진행하고 sudo certbot --apache -d rss.example.com을 실행하십시오. Certbot은 포트 443에 대한 가상 호스트를 재작성하고 리다이렉트를 추가합니다. curl -I https://rss.example.com/로 확인하십시오. 이 명령은 200을 반환하거나 로그인 페이지로 리다이렉트되어야 합니다.
명령줄에서 설치 프로그램 실행하기
FreshRSS는 브라우저 기반 설치 프로그램을 제공하지만, 명령줄 버전을 사용하면 설치 과정을 반복할 수 있고 선택한 항목을 정확하게 기록으로 남길 수 있습니다.
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'두 명령 모두 www-data 사용자로 실행하십시오. root 사용자로 실행하면 설정 파일의 소유권이 root로 지정되어, 나중에 웹 인터페이스에서 설정을 저장할 수 없게 됩니다. --environment production 설정도 중요합니다. 개발 모드로 설정하면 페이지에 PHP 알림 메시지가 출력되기 때문입니다.
https://rss.example.com/을 로드하고 admin 계정으로 로그인하십시오.
피드가 자동으로 새로고침되지 않는 이유
사용자가 지시하기 전까지는 어떤 피드도 폴링되지 않습니다. FreshRSS는 브라우저가 열려 있을 때만 새로고침을 수행하므로, 하루에 두 번 접속하는 인스턴스는 12시간 지난 기사를 보여주게 됩니다. 이 문제는 프로젝트에서 제공하는 스크립트인 app/actualize_script.php를 cron에서 실행하여 해결합니다.
sudo crontab -u www-data -e다음 한 줄을 추가하십시오:
*/20 * * * * php /srv/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&120분 주기가 적절한 최소 단위입니다. 스크립트는 특정 피드를 20분보다 자주 새로고침하지 않도록 설정되어 있으므로, 더 짧은 주기로 실행하면 CPU 자원만 낭비하게 됩니다. 먼저 수동으로 한 번 실행해 보십시오:
sudo -u www-data php /srv/freshrss/app/actualize_script.php정상적으로 작동하면 가져온 각 피드의 이름이 출력되고 PHP 오류 없이 종료됩니다. 아무런 출력도 나타나지 않는다면 cron 사용자가 잘못 설정된 것이며, data/에서 권한 오류가 발생한다면 chmod -R g+w 단계를 건너뛴 것입니다.
첫 번째 피드 추가하기
인터페이스 왼쪽 상단에 있는 더하기 버튼을 사용하십시오. 사이트 주소를 붙여넣으면 FreshRSS가 자동으로 피드 링크를 찾아냅니다. 대부분의 사이트는 피드를 명시적으로 홍보하지 않더라도 여전히 발행하고 있으며, 보통 /feed, /rss 또는 /atom.xml 경로에 위치합니다. 카테고리는 폴더와 같으며, 피드를 카테고리 간에 이동하려면 드래그하면 됩니다.
다른 리더를 사용 중이었다면 해당 서비스에서 OPML 파일을 내보낸 뒤, 구독 관리 페이지에서 가져오기를 수행하십시오. OPML(outline processor markup language)은 표준 피드 목록 형식이며, 계속 사용할 가치가 있는 모든 리더는 이를 지원합니다. 대량의 피드를 가져오면 첫 번째 새로 고침 시 모든 피드를 한 번씩 가져와야 하므로 속도가 느려질 수 있습니다. 따라서 속도를 판단하기 전에 첫 번째 cron 작업이 완료될 때까지 시간을 두고 기다리십시오.
휴대폰에서 읽기
FreshRSS는 거의 모든 RSS 앱이 지원하는 Google Reader API를 사용합니다. 두 가지 조건을 충족해야 합니다. 인증 설정에서 "Allow API access"가 활성화되어야 하며, 이는 위에서 언급한 --api-enabled 플래그로 이미 설정되었습니다. 프로필 설정의 API password 필드에는 값이 입력되어 있어야 합니다. 휴대폰은 분실하기 쉬운 기기이므로, 이 비밀번호는 로그인 비밀번호와 별도로 관리하는 것이 좋습니다.
https://rss.example.com/api/에 접속하여 "Check full server configuration"을 선택합니다. 정상적으로 설정되었다면 PASS이 표시됩니다. 여기서 실패한다면 대부분 AllowEncodedSlashes On 줄이 누락되었기 때문입니다. 앱에서는 서버 주소를 https://rss.example.com/api/greader.php로, 사용자 이름을 FreshRSS 계정으로, API password를 비밀번호로 입력하십시오.
Docker 대안
PHP와 Apache를 직접 관리하고 싶지 않다면, 프로젝트에서 공식적으로 제공하는 freshrss/freshrss 이미지를 사용할 수 있습니다. compose 파일 하나로 애플리케이션과 데이터베이스를 함께 구성할 수 있습니다. 이 방식은 일반적인 장단점을 가집니다. 호스트에서 관리할 요소는 줄어들지만, 문제가 발생했을 때 디버깅해야 할 계층이 하나 더 늘어납니다. 또한 TLS(transport layer security)를 위해 리버스 프록시가 여전히 필요합니다. 이 방식이 더 적합하다면, VPS를 위한 Docker Compose 기초에서 파일 형식을 확인하십시오. cron 라인은 docker exec --user www-data freshrss php ./app/actualize_script.php로 변경됩니다.
백업 및 업그레이드
상태를 유지하는 요소는 데이터베이스와 /srv/freshrss/data/ 두 가지입니다. sudo mysqldump freshrss > freshrss.sql를 사용하여 데이터베이스를 덤프하고, /srv/freshrss/data/을 복사한 뒤 두 항목 모두 이 서버가 아닌 다른 곳에 보관하십시오. 구독 목록은 가끔 OPML 형식으로 내보내는 것이 좋습니다. 해당 파일만 있으면 어떤 RSS 소프트웨어에서든 읽기 환경을 재구축할 수 있기 때문입니다.
업그레이드는 새 릴리스를 동일한 디렉터리에 압축 해제하고 소유권 변경 명령을 다시 실행하는 방식으로 진행합니다. FreshRSS는 다음 페이지 로드 시 자체적으로 데이터베이스 마이그레이션을 적용합니다. 복구할 수 없는 데이터베이스에서 마이그레이션이 실패하면 되돌릴 수 없으므로, 반드시 먼저 백업하십시오. RSS 리더는 운영 위험이 낮으며 직접 호스팅할 가치가 있는 서비스 목록을 따라가고 있다면 가장 먼저 시도해 볼 만한 좋은 후보입니다.
FAQ
왜 FreshRSS를 열 때만 피드가 업데이트됩니까?
스케줄러를 생성하기 전까지는 아무런 작업도 수행되지 않기 때문입니다. FreshRSS는 브라우저 세션이 열려 있는 동안에만 피드를 새로 고치며, 탭을 닫으면 아무 작업도 하지 않습니다. www-data 사용자로 app/actualize_script.php을 호출하는 cron 라인을 추가한 다음, 스크립트를 수동으로 한 번 실행하여 출력을 확인하십시오. 아무런 메시지가 없다면 cron이 잘못된 사용자로 실행 중이라 PHP가 data/에 쓰기 작업을 할 수 없는 상태일 가능성이 큽니다.
웹사이트는 잘 작동하는데 모바일 앱이 연결되지 않는 이유는 무엇입니까?
Google Reader API는 요청 경로 내에 인코딩된 슬래시(%2F)를 포함하는데, Apache는 기본적으로 이를 거부합니다. 가상 호스트 설정 내에 AllowEncodedSlashes On를 추가하고 Apache를 다시 로드하십시오. https://rss.example.com/api/를 열어 "Check full server configuration"을 실행했을 때 PASS이 표시되는지 확인하여 수정 사항을 검증하십시오. 또한 API 비밀번호는 로그인 비밀번호와 별개이므로, 프로필 설정에서 API 비밀번호가 올바르게 지정되었는지 확인하십시오.
SQLite와 MariaDB 중 무엇을 사용해야 합니까?
사용자가 한 명뿐이라면 설치할 것이 없고 비밀번호를 관리할 필요가 없는 SQLite를 권장합니다. 사용자가 여러 명이거나 피드 수가 수백 개를 넘어가면 MariaDB를 사용하십시오. 새로 고침 작업과 웹 인터페이스가 동시에 쓰기 작업을 수행할 때 단일 파일 잠금이 병목 현상을 일으키기 때문입니다. 나중에 내보내기 및 가져오기 명령을 통해 데이터베이스를 이전할 수 있으므로, 이 결정이 영구적인 것은 아닙니다.
설치 프로그램이 설정을 저장할 때 실패합니다. 무엇이 문제입니까?
PHP는 Apache 하에서 www-data로 실행되는데, 이 사용자는 root가 소유한 디렉터리에 쓰기 작업을 할 수 없습니다. sudo chown -R www-data:www-data /srv/freshrss 및 sudo chmod -R g+w /srv/freshrss/data를 다시 실행한 다음 설치 프로그램을 다시 시작하십시오. 만약 이미 root 권한으로 명령줄 설치 프로그램을 실행했다면, data/ 하위에 생성된 파일들의 소유권 문제로 오류가 발생하므로 해당 파일들을 삭제한 후 다시 시도하십시오.
자체 호스팅 RSS 리더를 운영하려면 어느 정도의 서버 사양이 필요합니까?
매우 낮은 사양으로도 충분합니다. 20분마다 짧게 HTTP 요청을 보내고 오래된 기사를 삭제하면 데이터베이스 크기가 작게 유지되므로, 소규모 플랜에서도 수백 개의 피드를 원활하게 운영할 수 있습니다. 디스크 사용량은 보관 기간에 따라 증가하므로, 모든 데이터를 영구적으로 보관하기보다는 아카이빙 설정에서 기사 삭제 정책을 설정하십시오.