SSD Nodes Learn 8GB RAM — 연 $66
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-01

Ubuntu 24.04 VPS에 FreshRSS 설치하기

Ubuntu 24.04 VPS에서 FreshRSS 1.29.1을 Apache, PHP 8.3, MariaDB로 설치합니다. 가상 호스트, cron 새로 고침, 인코딩된 슬래시와 모바일 앱 API 설정까지 다룹니다.

구축하는 항목

셀프 호스팅 RSS 리더는 사용자가 소유한 서버에서 실행되는 피드 리더입니다. 따라서 다른 사람이 이를 종료하거나 표시 내용을 변경할 수 없습니다. 이 가이드에서는 Ubuntu 24.04 VPS에 FreshRSS를 설치합니다. Apache를 프런트엔드로 사용하고, PHP를 백엔드로 사용하며, MariaDB에 데이터를 저장하고, 새 글을 가져오는 cron 작업 1개를 설정합니다. RSS(Really Simple Syndication)는 웹 사이트가 게시하는 글을 소프트웨어가 읽을 수 있도록 제공하는 파일 형식입니다. FreshRSS는 이러한 파일을 수집하고 글을 저장하며, 웹 인터페이스와 휴대폰 앱이 사용하는 API(Application Programming Interface)를 제공합니다.

설치 자체는 간단합니다. 릴리스를 압축 해제하고, 데이터베이스를 만들고, 가상 호스트 1개를 작성한 다음, 명령줄 설치 프로그램을 1회 실행하면 됩니다. 아래에서 대부분의 작업은 이후에 자주 잘못 설정하는 부분입니다. 여기에는 새로 고침 작업, 모바일 API를 위한 인코딩된 슬래시, 파일 소유권이 포함됩니다.

FreshRSS 1.29.1은 2026년 7월 현재 최신 릴리스이며 PHP 8.1 이상이 필요합니다. Ubuntu 24.04에는 PHP 8.3이 포함되어 있으므로 배포판 패키지만 사용하면 됩니다. 타사 PHP 저장소는 필요하지 않습니다.

작동하는 LAMP 스택에서 시작하기

FreshRSS는 일반적인 PHP 애플리케이션이므로 다른 PHP 애플리케이션과 동일한 기반이 필요합니다. 아직 이 기반을 구축하지 않았다면 먼저 Ubuntu 24.04용 LAMP 스택 설정을 수행한 후 이 문서로 돌아옵니다. 요약하면 다음과 같습니다.

sudo apt update
sudo apt install -y apache2 mariadb-server php libapache2-mod-php
sudo systemctl enable --now apache2 mariadb

systemctl 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 apache2

php -m로 PHP가 실제로 로드한 항목을 확인합니다. 확장이 누락되어도 설치 프로그램이 시작되지 않는 것은 아닙니다. 요구 사항 화면에서 해당 확장 이름이 표시된 빨간색 줄과 함께 중단됩니다. 이 화면에서 문제를 처음 발견하면 원인을 파악하기 어려우므로 지금 확인합니다. Apache를 다시 시작해야 합니다. libapache2-mod-php가 Apache 프로세스 내부에 PHP를 유지하기 때문입니다. 따라서 새로 설치한 확장은 Apache를 다시 시작하기 전까지 웹 서버에서 인식되지 않습니다.

릴리스 다운로드

FreshRSS를 기본 웹 루트 외부에 설치하고 Apache가 해당 위치를 가리키도록 설정합니다. 애플리케이션 디렉터리를 문서 루트와 분리하면 public 폴더만 HTTP를 통해 접근할 수 있습니다.

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는 설정이 필요하지 않으며, 수백 개의 피드를 사용하는 1명의 사용자에게 적합합니다. 여러 사용자가 인스턴스를 공유한다면 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 apache2

configtestSyntax OK를 출력해야 합니다. AllowEncodedSlashes On은 선택 사항처럼 보이지만 그렇지 않습니다. Google Reader API는 %2F을 포함하는 피드 식별자를 전송합니다. 이 지시문이 없으면 Apache가 해당 식별자를 거부합니다. 따라서 웹 인터페이스는 정상적으로 작동하지만 모바일 앱은 동기화에 실패합니다.

로그인하기 전에 HTTPS 추가

이 사이트에 암호를 입력하기 전에 먼저 인증서를 발급받아야 합니다. A 레코드가 서버를 가리키도록 설정한 다음 Ubuntu에서 Apache용 Certbot 설정을 따르고 sudo certbot --apache -d rss.example.com을 실행합니다. Certbot은 포트 443용 virtual host 설정을 다시 작성하고 리디렉션을 추가합니다. 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>&1

20분이 적절한 최소 간격입니다. 이 스크립트는 개별 피드를 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를 제공합니다. 다음 2가지 조건을 충족해야 합니다. 인증 설정에서 "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 암호로 입력합니다.

Docker 대안

PHP와 Apache를 직접 관리하지 않으려는 경우 프로젝트에서 공식 freshrss/freshrss 이미지를 배포하며, compose 파일 하나로 애플리케이션과 데이터베이스를 함께 구성할 수 있습니다. 일반적인 장단점은 다음과 같습니다. 호스트의 구성 요소는 줄어들지만, 문제가 발생했을 때 디버깅해야 할 계층이 하나 늘어납니다. 또한 TLS(전송 계층 보안)를 사용하려면 reverse proxy가 여전히 필요합니다. 이 방식이 더 적합하다면 VPS용 Docker Compose 기본 사항에서 파일 형식을 확인할 수 있으며, cron 항목은 docker exec --user www-data freshrss php ./app/actualize_script.php이 됩니다.

백업 및 업그레이드

상태를 보존하는 요소는 데이터베이스와 /srv/freshrss/data/입니다. 첫 번째 요소는 sudo mysqldump freshrss > freshrss.sql로 덤프하고, 두 번째 요소는 복사합니다. 두 백업 모두 이 서버가 아닌 다른 위치에 보관합니다. 구독 목록도 가끔 OPML로 내보낼 가치가 있습니다. 이 파일을 사용하면 어떤 RSS 소프트웨어에서든 읽기 환경을 다시 구성할 수 있습니다.

업그레이드는 동일한 디렉터리에 새 릴리스를 압축 해제한 후 소유권 명령을 다시 실행하는 방식으로 진행합니다. FreshRSS는 다음 페이지를 로드할 때 자체 데이터베이스 마이그레이션을 적용합니다. 먼저 백업해야 합니다. 복원할 수 없는 데이터베이스에서 마이그레이션이 실패하면 복구할 수 없기 때문입니다. 리더는 낮은 위험으로 운영할 수 있는 서비스이며, 셀프 호스팅할 가치가 있는 항목 목록을 검토하는 경우 처음 시도하기에 적합한 대상입니다.

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를 사용합니다. 설치할 항목이 없고 관리할 비밀번호도 없기 때문입니다. 인스턴스에서 1명보다 많은 사람이 읽거나 피드가 수백 개를 넘으면 MariaDB를 사용합니다. 새로 고침 작업과 웹 인터페이스가 동시에 쓰기 작업을 수행하므로 단일 파일 잠금이 제한 요인이 됩니다. 나중에 내보내기 및 가져오기 명령을 사용하여 두 데이터베이스 사이를 이동할 수 있으므로, 이는 영구적인 결정이 아닙니다.

설치 프로그램이 구성을 기록할 때 실패합니다. 무엇이 잘못되었습니까?

Apache에서 PHP는 www-data로 실행되며, 이 사용자는 root가 소유한 디렉터리에 쓸 수 없습니다. sudo chown -R www-data:www-data /srv/freshrsssudo chmod -R g+w /srv/freshrss/data를 다시 실행한 다음 설치 프로그램을 다시 시작합니다. 이미 root로 명령줄 설치 프로그램을 실행했다면, 다시 시도하기 전에 data/ 아래에 설치 프로그램이 생성한 파일을 삭제합니다. 실제 문제는 해당 파일의 소유권이기 때문입니다.

자체 호스팅 RSS 리더에 필요한 서버 자원은 어느 정도입니까?

매우 적습니다. 소규모 요금제에서 피드 수백 개를 운영해도 충분합니다. 부하는 20분마다 짧게 발생하는 HTTP 가져오기 작업이며, 오래된 기사를 삭제하면 데이터베이스도 작게 유지되기 때문입니다. 디스크 사용량은 보존 기간에 따라 증가하므로, 모든 항목을 영구적으로 보관하지 말고 보관 설정에서 기사 삭제 정책을 지정합니다.

#freshrss#rss#self-hosting#php#apache