Ubuntu 24.04에 FreshRSS 설치 및 설정 가이드
Ubuntu 24.04 VPS에서 Apache, PHP, MariaDB를 사용하여 FreshRSS를 직접 구축합니다. 릴리스 압축 파일 설치부터 가상 호스트 설정, cron 자동 새로고침 구성 및 모바일 API 연동까지 단계별로 안내합니다.
구축할 시스템
자체 호스팅 RSS 리더는 사용자가 소유한 서버에서 직접 실행되는 피드 리더입니다. 따라서 외부 요인으로 서비스가 중단되거나 표시되는 콘텐츠가 임의로 변경되지 않습니다. 이 가이드에서는 Ubuntu 24.04 VPS에 FreshRSS를 설치하는 방법을 다룹니다. 구성 요소로는 앞단에 Apache, 뒷단에 PHP, 데이터베이스로 MariaDB를 사용하며, 새로운 기사를 가져오기 위해 하나의 cron job을 설정합니다. RSS(Really Simple Syndication)는 웹사이트가 기사를 배포하기 위해 사용하는 파일 형식이며, 소프트웨어가 이를 읽어 들입니다. FreshRSS는 이러한 파일을 수집하고 기사를 저장하며, 웹 인터페이스와 모바일 앱이 통신할 수 있는 API(Application Programming Interface)를 제공하는 PHP 애플리케이션입니다.
설치 과정은 간단합니다. 릴리스 파일을 압축 해제하고, 데이터베이스를 생성한 뒤, 가상 호스트를 설정하고, 명령줄 설치 프로그램을 실행한 다음 cli/create-user.php를 사용하여 계정을 생성하면 됩니다. 아래 설명하는 작업의 대부분은 설치 후 흔히 발생하는 실수인 새로고침 작업, 모바일 API를 위한 슬래시 인코딩, 파일 소유권 문제 해결에 집중되어 있습니다.
2026년 10월 21일에 배포된 FreshRSS 1.30.4가 2026년 10월 29일 기준 최신 릴리스이며, 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.30.4.tar.gz
tar xzf 1.30.4.tar.gz
sudo mv FreshRSS-1.30.4 /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는 php-sqlite3 확장이 필요하지만 별도의 데이터베이스 서버 설정은 필요 없으며, 각 사용자의 데이터를 개별 data/users/_user_/db.sqlite 파일에 저장합니다. 사용자를 추가해도 데이터가 하나의 공유 SQLite 파일에 기록되지 않습니다. 이 가이드에서는 LAMP 스택의 일부로 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바이트 문자를 저장할 수 없습니다. MariaDB의 기본 엄격 모드(strict mode)에서는 지원되지 않는 문자가 포함될 경우 오류가 발생하며, 데이터 절단 여부는 SQL 모드 및 IGNORE와 같은 문장 옵션에 따라 달라집니다.
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를 연다고 해서 피드 가져오기 작업이 예약되지는 않습니다. 스케줄러가 없다면 내비게이션 메뉴의 업데이트 링크를 사용하여 수동으로 기사를 가져와야 합니다. 이 문제를 해결하려면 프로젝트에서 제공하는 스크립트인 app/actualize_script.php을 cron에서 실행해야 합니다.
sudo crontab -u www-data -e다음 한 줄을 추가하십시오:
*/20 * * * * php /srv/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&120분 정도가 적절한 주기이지만, 각 피드의 새로고침 간격 설정은 여전히 유효합니다. 기본값은 1시간이며, 개별 피드는 최소 15분 간격까지 지원합니다. 먼저 수동으로 한 번 실행해 보십시오:
sudo -u www-data php /srv/freshrss/app/actualize_script.php정상적인 출력은 새로고침 실행 결과를 보고하며 PHP 오류 없이 종료됩니다. 기사가 추가되거나 업데이트되면 피드 이름이 표시됩니다. 아무런 출력도 나타나지 않는다면 PHP 오류 출력과 FreshRSS 로그를 확인하십시오. 단순히 아무런 메시지가 없다는 것만으로는 cron 사용자가 원인인지 파악할 수 없습니다. data/에서 권한 오류가 발생하면 소유권과 쓰기 권한을 확인해야 합니다.
첫 번째 피드 추가하기
인터페이스 왼쪽 상단에 있는 더하기 버튼을 사용하십시오. 사이트 주소를 붙여넣으면 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/에 접속하여 서버 구성 검사를 자동으로 실행하십시오. 정상적인 설정이라면 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로 내보내는 것이 좋습니다. 해당 파일을 사용하면 OPML을 지원하는 다른 리더기로 피드 목록을 옮길 수 있기 때문입니다. 다만 이 방식은 새로 고침 빈도, 자격 증명, 사용자 에이전트, XPath 웹 스크래핑 규칙과 같은 FreshRSS 고유 설정은 보존하지 않습니다. 이러한 설정까지 보존하려면 SQLite 내보내기 기능을 사용하십시오.
업그레이드는 동일한 디렉터리에 최신 릴리스를 압축 해제하고 소유권 변경 명령을 다시 실행하는 방식으로 진행합니다. 프로젝트에서 권장하는 버전별 업그레이드 경로를 따르고, 각 단계마다 FreshRSS가 정상 작동하는지 확인하십시오. FreshRSS는 페이지를 새로 불러올 때 자체적으로 데이터베이스 마이그레이션을 적용합니다. 마이그레이션 실패 시 복구할 수 없는 상황이 발생할 수 있으므로 반드시 먼저 백업하십시오. 1.29.1과 같은 이전 tarball 버전에서 설치했다면 1.30.4로 이동하십시오. 1.30.x 보안 릴리스에는 인증 변경 시 세션 ID 교체 및 ZIP 가져오기 제한 기능이 포함되어 있으며, 1.30.4 버전은 심각한 PHP 취약점을 해결하기 위해 대체 Alpine Docker 이미지도 업데이트합니다. 리더기는 운영 위험이 낮은 서비스이므로 직접 호스팅할 가치가 있는 서비스 목록을 검토 중이라면 첫 번째 후보로 적합합니다.
FAQ
피드가 FreshRSS를 열 때만 업데이트되는 이유는 무엇입니까?
페이지를 여는 것만으로는 피드 가져오기 작업이 예약되지 않습니다. 스케줄러가 없다면 업데이트 링크를 사용하여 수동으로 새로 고침해야 합니다. www-data 사용자로 app/actualize_script.php를 호출하는 cron 라인을 추가한 다음, 스크립트를 직접 한 번 실행하여 출력을 확인하십시오. 아무런 출력이 없다면 PHP 오류 출력과 FreshRSS 로그를 확인하고, cron 사용자가 data/에 대한 쓰기 권한을 가지고 있는지 검증하십시오.
모바일 앱은 연결되지 않는데 웹사이트는 잘 작동합니다. 이유가 무엇입니까?
Google Reader API는 요청 경로 내에 인코딩된 슬래시(%2F)를 포함하는데, Apache는 기본적으로 이를 거부합니다. 가상 호스트 내에 AllowEncodedSlashes On를 추가하고 Apache를 다시 로드하십시오. https://rss.example.com/api/을 열어 자동 설정 검사 결과에 PASS이 표시되는지 확인하여 수정 사항을 검증하십시오. 또한 API 비밀번호는 로그인 비밀번호와 별개이므로, 프로필에서 API 비밀번호가 설정되어 있는지 확인하십시오.
SQLite와 MariaDB 중 무엇을 사용해야 합니까?
SQLite는 php-sqlite3 확장이 필요하지만, 별도의 데이터베이스 서버나 관리할 데이터베이스 비밀번호는 필요하지 않습니다. 각 사용자는 별도의 SQLite 데이터베이스 파일을 가지므로, 두 번째 사용자를 추가해도 하나의 공유 파일에서 경합이 발생하지 않으며 MariaDB도 필요하지 않습니다. LAMP 스택에서 데이터베이스 서버를 사용하려는 경우 MariaDB를 사용하십시오. 나중에 export 및 import 명령을 통해 두 방식 간에 전환할 수 있으므로, 이 결정이 영구적인 것은 아닙니다.
설치 프로그램이 설정을 기록할 때 실패합니다. 무엇이 문제입니까?
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 리더에는 어느 정도의 서버 사양이 필요합니까?
매우 낮은 사양으로도 충분합니다. FreshRSS는 보급형 하드웨어에서 실행되도록 설계되었지만, 리소스 사용량은 피드 개수, 기사 크기, 보관 설정에 따라 달라집니다. 디스크 사용량은 보관 기간에 따라 증가하므로, 모든 데이터를 영구적으로 보관하기보다는 아카이빙 설정에서 기사 삭제 정책을 설정하십시오.