Instalar FreshRSS en un VPS Ubuntu con Apache
Instala FreshRSS 1.29.1 en Ubuntu 24.04 con Apache, PHP 8.3 y MariaDB: tarball, host virtual, cron de actualización y API para la app móvil.
Qué va a instalar
Un lector RSS autoalojado es un lector de feeds que se ejecuta en un servidor propio, por lo que nadie puede apagarlo ni cambiar lo que muestra. Esta guía instala FreshRSS en un VPS con Ubuntu 24.04: Apache delante, PHP detrás, MariaDB para el almacenamiento y un trabajo de cron que obtiene artículos nuevos. RSS (really simple syndication) es el formato de archivo que publica un sitio para que el software pueda leer sus artículos. FreshRSS es la aplicación PHP que recopila esos archivos, conserva los artículos y ofrece una interfaz web y una API (application programming interface) que utilizan las aplicaciones móviles.
La instalación es sencilla: descomprimir una versión, crear una base de datos, escribir un host virtual y ejecutar un instalador desde la línea de comandos. La mayor parte del trabajo posterior corresponde a los errores más habituales: la tarea de actualización, las barras codificadas en la API móvil y la propiedad de los archivos.
FreshRSS 1.29.1 es la versión actual en julio de 2026 y necesita PHP 8.1 o posterior. Ubuntu 24.04 incluye PHP 8.3, por lo que los paquetes de la distribución son suficientes y no necesita un repositorio de PHP de terceros.
Comenzar con una pila LAMP operativa
FreshRSS es una aplicación PHP estándar, por lo que necesita la misma base que cualquier otra. Si todavía no ha configurado esa base, siga primero la configuración de la pila LAMP para Ubuntu 24.04 y vuelva aquí. En resumen:
sudo apt update
sudo apt install -y apache2 mariadb-server php libapache2-mod-php
sudo systemctl enable --now apache2 mariadbsystemctl status apache2 debería mostrar active (running). Si Apache no se inicia, la causa habitual es que otro proceso ya está utilizando el puerto 80; sudo ss -ltnp | grep :80 muestra cuál es.
Extensiones de PHP que necesita FreshRSS
FreshRSS considera obligatorias las extensiones libxml, cURL, JSON, PDO_MySQL, PCRE y ctype. También necesita mbstring, iconv, Zlib y ZipArchive, además de GMP en un sistema de 32 bits. En Ubuntu se instalan como paquetes de la distribución:
sudo apt install -y php-curl php-mbstring php-xml php-zip php-mysql php-intl php-gmp
sudo systemctl restart apache2Compruebe qué extensiones ha cargado realmente PHP con php -m. Si falta una extensión, el instalador no deja de iniciarse. Se detiene en la pantalla de requisitos y muestra una línea roja con el nombre de la extensión. Es un lugar confuso para descubrir el problema, por lo que debe comprobarlo ahora. Reiniciar Apache es importante porque libapache2-mod-php mantiene PHP dentro del proceso de Apache. Por eso, una extensión recién instalada no está disponible para el servidor web hasta que se reinicia.
Descargar la versión
Instala FreshRSS fuera de la raíz web predeterminada y configura Apache para que apunte a ella. Mantener el directorio de la aplicación separado de la raíz de documentos garantiza que solo la carpeta pública sea accesible mediante 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/freshrssAhora, los permisos, que la documentación de FreshRSS exige estrictamente: el usuario del servidor web es propietario de todo el árbol, el grupo puede leer todos los archivos y el grupo puede escribir en ./data/.
sudo chown -R www-data:www-data /srv/freshrss
sudo chmod -R g+r /srv/freshrss
sudo chmod -R g+w /srv/freshrss/dataSi omites este paso, el instalador falla al escribir su configuración, porque PHP se ejecuta como www-data y www-data no puede escribir en un directorio propiedad de root.
Cree la base de datos
FreshRSS admite SQLite, MariaDB, MySQL y PostgreSQL. SQLite no requiere configuración y es adecuado para una persona con unos cientos de feeds. MariaDB es la mejor opción cuando varias personas comparten la instancia, porque las escrituras simultáneas del trabajo de actualización y de la interfaz web ya no compiten por un único bloqueo de archivo.
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;"Use utf8mb4 y no utf8. Los feeds contienen emoji y scripts no latinos, y la codificación antigua de tres bytes utf8 trunca el título de un artículo en el primer carácter de cuatro bytes.
El host virtual de Apache
El directorio público es p/, no la raíz del árbol. Todo lo demás, incluido el archivo de configuración que contiene la contraseña de la base de datos, se encuentra por encima de la raíz de documentos, donde Apache nunca lo servirá.
<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>Guárdalo como /etc/apache2/sites-available/freshrss.conf y actívalo:
sudo a2enmod rewrite
sudo a2ensite freshrss
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2configtest debería mostrar Syntax OK. AllowEncodedSlashes On parece opcional, pero no lo es: la API de Google Reader envía identificadores de feeds que contienen %2F y, sin esta directiva, Apache los rechaza. Por eso las aplicaciones móviles no pueden sincronizarse, mientras que la interfaz web funciona correctamente.
Añade HTTPS antes de iniciar sesión
Vas a escribir una contraseña en este sitio, así que obtén primero un certificado. Apunta un registro A al servidor, sigue la configuración de Certbot para Apache en Ubuntu y ejecuta sudo certbot --apache -d rss.example.com. Certbot reescribe el host virtual para el puerto 443 y añade la redirección. Confírmalo con curl -I https://rss.example.com/, que debería devolver un 200 o redirigir a la página de inicio de sesión.
Ejecute el instalador desde la línea de comandos
FreshRSS tiene un instalador en el navegador, pero la versión de línea de comandos se puede repetir y deja un registro exacto de las opciones seleccionadas.
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'Ejecute ambos como www-data. Si los ejecuta como root, se crean archivos de configuración propiedad de root y la interfaz web no puede guardar ninguna configuración posteriormente. --environment production también es importante, porque la configuración de desarrollo muestra avisos de PHP en la página.
Cargue https://rss.example.com/ e inicie sesión como admin.
Por qué los feeds no se actualizan automáticamente
Nada consulta tus feeds hasta que se lo indicas. FreshRSS se actualiza mientras hay un navegador abierto en él. Por eso, una instancia que visitas dos veces al día te muestra artículos con doce horas de antigüedad. La solución es el script que el proyecto proporciona para esta tarea, app/actualize_script.php, ejecutado desde cron.
sudo crontab -u www-data -eAñade una línea:
*/20 * * * * php /srv/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&1Veinte minutos es el intervalo mínimo razonable. El script no actualiza ningún feed más de una vez cada veinte minutos. Un intervalo menor solo consume CPU. Ejecútalo manualmente una vez:
sudo -u www-data php /srv/freshrss/app/actualize_script.phpLa salida correcta muestra el nombre de cada feed que ha descargado y termina sin un error de PHP. Si no muestra nada, el usuario de cron es incorrecto. Un error de permisos en data/ significa que se omitió el paso chmod -R g+w.
Agrega tus primeros feeds
Usa el botón de suma situado en la parte superior izquierda de la interfaz, pega la dirección de un sitio y FreshRSS buscará el enlace del feed. La mayoría de los sitios todavía publican uno aunque no lo anuncien, normalmente en /feed, /rss o /atom.xml. Las categorías son carpetas y mover un feed entre ellas se hace mediante arrastrar y soltar.
Si vienes de otro lector, exporta allí un archivo OPML e impórtalo desde la página de administración de suscripciones. OPML (outline processor markup language) es el formato estándar para las listas de feeds, y todos los lectores que merece la pena usar admiten este formato. Una importación grande es lenta durante la primera actualización porque se obtiene cada feed una vez. Espera a que se ejecute el primer cron antes de evaluar la velocidad.
Léelo en tu teléfono
FreshRSS es compatible con la Google Reader API, que admiten casi todas las aplicaciones RSS. Deben cumplirse dos condiciones. En la configuración de autenticación, "Allow API access" debe estar activado; el indicador --api-enabled anterior ya lo activó. En tu perfil, el campo de contraseña de la API debe contener un valor. Esta contraseña es independiente de la contraseña de inicio de sesión porque un teléfono se pierde con más facilidad.
Visita https://rss.example.com/api/ y selecciona "Check full server configuration". Una configuración operativa devuelve PASS. Si esta comprobación falla, casi siempre falta la línea AllowEncodedSlashes On. En la aplicación, indica https://rss.example.com/api/greader.php como dirección del servidor, tu usuario de FreshRSS como nombre de usuario y la contraseña de la API como contraseña.
La alternativa con Docker
Si prefiere no mantener PHP y Apache manualmente, el proyecto publica una imagen oficial de freshrss/freshrss y un archivo de compose le proporciona la aplicación y su base de datos juntas. La contrapartida es la habitual: menos componentes que mantener en el host, una capa adicional que depurar cuando algo falla y la necesidad de usar un proxy inverso para TLS (seguridad de la capa de transporte). Si esta opción se adapta mejor a sus necesidades, los conceptos básicos de Docker Compose para un VPS explican el formato del archivo, y la línea de cron se convierte en docker exec --user www-data freshrss php ./app/actualize_script.php.
Copias de seguridad y actualizaciones
Dos elementos conservan el estado: la base de datos y /srv/freshrss/data/. Vuelque la primera con sudo mysqldump freshrss > freshrss.sql, copie el segundo y conserve ambos en una ubicación distinta de este servidor. También conviene exportar ocasionalmente la lista de suscripciones a OPML, porque ese archivo reconstruye la configuración de lectura en cualquier software RSS.
Actualizar consiste en desempaquetar una versión más reciente sobre el mismo directorio y volver a ejecutar los comandos de propiedad. FreshRSS aplica sus propias migraciones de base de datos al cargar la siguiente página. Haga primero una copia de seguridad, porque una migración fallida en una base de datos que no puede restaurar deja el sistema en una situación irrecuperable. Un lector RSS es un servicio de bajo riesgo y un buen candidato inicial si está revisando una lista de servicios que merece la pena autoalojar.
FAQ
¿Por qué mis feeds solo se actualizan cuando abro FreshRSS?
Porque no existe ningún programador hasta que cree uno. FreshRSS actualiza los feeds mientras hay una sesión del navegador abierta y no hace nada cuando se cierra la pestaña. Añada la línea de cron que llama a app/actualize_script.php como usuario www-data, y ejecute el script manualmente una vez para leer su salida. La ausencia de salida suele indicar que cron lo ejecuta con el usuario incorrecto, por lo que PHP no puede escribir en data/.
Mi aplicación móvil no puede conectarse, pero el sitio web funciona. ¿Por qué?
La API de Google Reader incluye barras codificadas (%2F) en las rutas de las solicitudes, y Apache las rechaza de forma predeterminada. Añada AllowEncodedSlashes On dentro del host virtual y recargue Apache. Confirme la corrección abriendo https://rss.example.com/api/ y ejecutando "Check full server configuration", que debería informar PASS. Compruebe también que la contraseña de la API esté configurada en su perfil, ya que es independiente de la contraseña de inicio de sesión.
¿Debo usar SQLite o MariaDB?
SQLite para un solo usuario, porque no hay nada que instalar ni ninguna contraseña que administrar. Use MariaDB cuando más de una persona lea en la instancia o cuando supere unos cientos de feeds, porque el trabajo de actualización y la interfaz web escriben al mismo tiempo, y un único bloqueo de archivo se convierte en el límite. Más adelante puede cambiar entre ambos mediante los comandos de exportación e importación, por lo que no es una decisión permanente.
El instalador falla al escribir su configuración. ¿Cuál es el problema?
PHP se ejecuta como www-data bajo Apache, y ese usuario no puede escribir en un directorio propiedad de root. Vuelva a ejecutar sudo chown -R www-data:www-data /srv/freshrss y sudo chmod -R g+w /srv/freshrss/data, y después inicie de nuevo el instalador. Si ya ejecutó el instalador de línea de comandos como root, elimine los archivos que creó en data/ antes de volver a intentarlo, porque su propietario es el problema real.
¿Cuántos recursos de servidor necesita un lector RSS autoalojado?
Muy pocos. Unos cientos de feeds funcionan cómodamente en un plan pequeño, porque la carga consiste en ráfagas breves de solicitudes HTTP cada veinte minutos y la base de datos sigue siendo pequeña cuando se eliminan los artículos antiguos. El uso de disco aumenta según el periodo de conservación, por lo que debe establecer una política de eliminación de artículos en la configuración de archivado en lugar de conservarlos indefinidamente.