Tutoriales de VPS
Alojar FreshRSS en un VPS (2026): Docker Compose, HTTPS con Caddy, actualización automática y sincronización móvil
Este tutorial explica cómo alojar FreshRSS en un VPS para crear un lector RSS ligero y sin interferencias algorítmicas. Cubre el despliegue con Docker Compose, la opción predeterminada con SQLite, HTTPS automático con Caddy, la actualización automática con CRON_MIN, la API Google Reader para móviles, la combinación con RSSHub, copias de seguridad, actualizaciones, refuerzo de seguridad y resolución de problemas comunes.

Alojar FreshRSS en un VPS (2026): Docker Compose, HTTPS con Caddy, actualización automática y sincronización móvil
Muchas personas vuelven a usar RSS, no por nostalgia, sino porque el flujo de información cada vez cansa más. Las plataformas sociales meten contenido según el algoritmo, los resultados de búsqueda están cada vez más llenos de anuncios y basura de IA, y los newsletters fácilmente convierten tu correo en un segundo vertedero.
La ventaja de RSS es sencilla: lees lo que suscribes. Sin flujo de recomendaciones, sin secuestro por listas de tendencias, y sin necesidad de entregar tus hábitos de lectura a una plataforma de terceros.
FreshRSS es un lector RSS autoalojado muy adecuado para un VPS. Es ligero, de código abierto, admite múltiples usuarios, admite sincronización web y móvil, y puede combinarse con herramientas como RSSHub y RSS-Bridge para incluir en tus suscripciones sitios que no ofrecen RSS.
Este artículo trata del despliegue de FreshRSS en un VPS con Docker Compose y del uso de Caddy para obtener HTTPS automáticamente. Usaremos la opción predeterminada de SQLite, la más simple y estable, adecuada para individuos y equipos pequeños; si tienes un volumen de suscripciones muy grande, más adelante también explicaremos cuándo deberías considerar PostgreSQL.
Es adecuado para estos escenarios:
- Quieres crear tu propio sustituto de Google Reader / Inoreader / Feedly.
- Quieres que tus fuentes, estados de lectura y artículos guardados estén en tu propio servidor.
- Quieres sincronizar el progreso de lectura entre el navegador del ordenador, la app del móvil y la tablet.
- Necesitas suscribirte a blogs, sitios técnicos, GitHub Releases, fuentes de noticias y actualizaciones de pódcasts.
- Quieres combinarlo con RSSHub para suscribirte a algunos sitios en chino que originalmente no tienen RSS.
No es tan adecuado para estos escenarios:
- Solo lees algunos artículos de vez en cuando y los marcadores del navegador te bastan.
- No quieres mantener en absoluto un VPS, copias de seguridad y HTTPS.
- Necesitas permisos de equipo muy complejos, colaboración con comentarios o funciones de base de conocimiento.
- Principalmente quieres archivar instantáneas de páginas web; Linkwarden o Wallabag se acercarían más a tus necesidades.
FreshRSS se posiciona como un "lector", no como un sistema de archivo web. Lo que mejor hace es obtener fuentes, sincronizar estados de lectura y conservar listas de artículos a largo plazo.
Los servicios RSS de terceros ahorran preocupaciones, pero tienen varios problemas:
- Las versiones gratuitas suelen limitar el número de suscripciones, la frecuencia de actualización y las funciones de búsqueda.
- Tus datos e historial de lectura están en la plataforma de otro.
- Algunos servicios no admiten bien sitios en chino, el acceso desde la red de China continental ni las fuentes de RSSHub.
- Cuando un servicio cierra o sube de precio, migrar es molesto.
Las ventajas de autoalojar FreshRSS son: el OPML de suscripciones se puede exportar en cualquier momento, los datos están en tu propio VPS, tú decides la frecuencia de actualización y los clientes móviles pueden sincronizar mediante una API estándar.
FreshRSS es muy ligero. La imagen Docker oficial admite amd64, arm64 y arm/v7, y el uso personal tiene requisitos de máquina muy bajos.
| Escenario | Configuración recomendada |
|---|---|
| Uso personal, de decenas a cientos de fuentes | 1 vCPU / 512MB-1GB RAM / 10GB SSD |
| Varios usuarios o combinado con RSSHub | 1-2 vCPU / 1-2GB RAM / 20GB SSD |
| Muchas fuentes, retención de texto completo a largo plazo | 2 vCPU / 2GB RAM / 40GB+ SSD |
Si tu VPS ya ejecuta servicios ligeros como Caddy, Uptime Kuma, Vaultwarden o Umami, FreshRSS puede ejecutarse junto a ellos. Lo que realmente ocupa espacio es la caché de artículos, la caché de imágenes, la base de datos SQLite y el directorio de extensiones, no el programa FreshRSS en sí.
FreshRSS admite SQLite, PostgreSQL y MySQL/MariaDB.
Para un tutorial de VPS personal recomiendo SQLite por defecto, por razones sencillas:
- Un contenedor de base de datos menos.
- Hacer copia de seguridad es hacer copia de seguridad del directorio
data. - La migración es sencilla.
- Es completamente suficiente para 1-5 usuarios.
¿Cuándo considerar PostgreSQL?
- Tienes muchos usuarios.
- Tienes muchísimas fuentes.
- Ya tienes un servicio PostgreSQL unificado.
- Quieres una gestión más estandarizada de copias de seguridad, monitorización y permisos de la base de datos.
Este artículo usa la opción predeterminada de SQLite. No es una "solución de juguete", sino la solución más despreocupada para un lector RSS personal.
Este artículo asume:
- Ubuntu 22.04 / 24.04 o Debian 12.
- Docker y Docker Compose V2 instalados.
- Tienes un dominio, por ejemplo
rss.example.com. - El registro A del dominio apunta a la IP pública del VPS.
- Los puertos 80 y 443 pueden abrirse para que Caddy obtenga HTTPS automáticamente.
Crear los directorios:
sudo mkdir -p /opt/freshrss
sudo chown -R "$USER":"$USER" /opt/freshrss
cd /opt/freshrss
mkdir -p data extensions backups caddy-data caddy-config
Propósito de los directorios:
| Directorio | Propósito |
|---|---|
data | Configuración de FreshRSS, datos de usuario, base de datos SQLite, caché |
extensions | Extensiones de terceros |
backups | Copias de seguridad locales |
caddy-data | Certificados y estado de Caddy |
caddy-config | Datos de configuración de Caddy |
Como este artículo usa bind mounts para montar directorios del host en el contenedor, también hay que gestionar los permisos de escritura. El usuario web dentro del contenedor FreshRSS suele ser www-data, con UID/GID numéricos habitualmente 33:33. En un VPS Linux se recomienda ejecutar:
sudo chown -R 33:33 data extensions
sudo chmod -R u+rwX,g+rwX data extensions
Si el UID de www-data es diferente en tu entorno, puedes entrar primero al contenedor para confirmarlo, o cambiar a volúmenes con nombre de Docker. Los volúmenes con nombre se acercan más al ejemplo oficial de Compose y tienen menos problemas de permisos; la ventaja del bind mount es que las rutas son más intuitivas al hacer copias de seguridad y migrar.
La documentación oficial exige explícitamente hacer copia de seguridad de data, y si usas extensiones de terceros, también se recomienda hacer copia de seguridad de extensions.
Crear .env:
nano .env
Escribir:
FRESHRSS_DOMAIN=rss.example.com
TZ=Asia/Shanghai
CRON_MIN=7,37
Explicación:
FRESHRSS_DOMAIN: el dominio usado por Caddy.TZ=Asia/Shanghai: usar la zona horaria de China para evitar que las horas de los artículos y de actualización parezcan incorrectas.CRON_MIN=7,37: actualizar las fuentes en el minuto 7 y el minuto 37 de cada hora, es decir, aproximadamente una vez cada 30 minutos.
La documentación oficial de FreshRSS indica que el script de actualización no hará que una fuente se actualice con demasiada frecuencia, así que no hace falta configurar la tarea programada demasiado seguida. Para uso personal, una vez cada 20-30 minutos es suficiente.
Crear docker-compose.yml:
nano docker-compose.yml
Escribir:
services:
freshrss:
image: freshrss/freshrss:latest
container_name: freshrss
restart: unless-stopped
env_file:
- .env
environment:
TZ: ${TZ}
CRON_MIN: ${CRON_MIN}
FRESHRSS_ENV: production
volumes:
- ./data:/var/www/FreshRSS/data
- ./extensions:/var/www/FreshRSS/extensions
ports:
- "127.0.0.1:8080:80"
logging:
options:
max-size: 10m
healthcheck:
test: ["CMD", "cli/health.php"]
timeout: 10s
start_period: 60s
interval: 75s
retries: 3
caddy:
image: caddy:2-alpine
container_name: freshrss-caddy
restart: unless-stopped
env_file:
- .env
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
depends_on:
- freshrss
Aquí FreshRSS se vincula deliberadamente a 127.0.0.1:8080, de modo que el puerto 8080 no es accesible directamente desde internet. Lo que realmente se expone al exterior es el 80/443 de Caddy.
Si buscas reproducibilidad a largo plazo, en producción puedes fijar freshrss/freshrss:latest a una etiqueta de versión concreta, por ejemplo freshrss/freshrss:1.29.1. La documentación oficial indica que latest es la última versión estable y edge es la rama continua; no se recomienda a principiantes usar edge directamente en producción.
Crear Caddyfile:
nano Caddyfile
Escribir:
{$FRESHRSS_DOMAIN} {
encode zstd gzip
reverse_proxy freshrss:80
}
Abrir el firewall:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Si tu proveedor de nube tiene grupos de seguridad, también hay que permitir 80 y 443 allí. Cuando Caddy falla al obtener el certificado, lo primero que hay que revisar es el DNS, el firewall del sistema y los grupos de seguridad de la nube.
La documentación oficial de Caddy de FreshRSS también admite despliegue en subdirectorio, pero no recomiendo a principiantes montarlo bajo una ruta como /rss/. Un subdominio como rss.example.com es más sencillo y evita problemas con cookies, la API y la sincronización móvil.
Ejecutar:
cd /opt/freshrss
docker compose up -d
Ver el estado:
docker compose ps
Ver los registros:
docker compose logs -f --tail=100 freshrss
Abrir el navegador y visitar:
https://rss.example.com
La primera vez verás el asistente de instalación. Opciones recomendadas:
- Idioma: chino simplificado o English.
- Base de datos: SQLite.
- Cuenta de administrador: no uses una contraseña débil.
- Dirección del sitio: introduce el dominio HTTPS real.
Si aparecen problemas de permisos en el asistente de instalación, revisa primero si data y extensions se han concedido al usuario www-data dentro del contenedor. Con el enfoque de bind mount de este artículo, la solución más común es volver a ejecutar:
cd /opt/freshrss
sudo chown -R 33:33 data extensions
Esta configuración de Compose ya ha habilitado cron dentro del contenedor mediante CRON_MIN=7,37.
Puedes observar las actualizaciones en los registros:
docker compose logs --tail=200 freshrss
FreshRSS ofrece oficialmente tres métodos de actualización:
- Cron dentro del contenedor: el más sencillo, usado en este artículo.
- Cron del host que llama a
actualize_script.php. - Un contenedor independiente solo para cron.
No actives los tres a la vez. Las actualizaciones duplicadas no harán que una sola fuente vaya infinitamente más rápido; solo añaden ruido en los registros y complejidad para diagnosticar.
FreshRSS admite añadir manualmente fuentes RSS/Atom y también importar OPML.
Si migras desde otro servicio RSS, el proceso habitual es:
- Exportar OPML desde el servicio antiguo.
- Importar OPML en el panel de administración de FreshRSS.
- Esperar una ronda de actualización automática.
- Revisar las fuentes que fallan.
- Organizar por categorías.
Se recomienda importar primero un lote pequeño de prueba, no miles de fuentes de una vez. Muchos OPML antiguos contienen sitios ya inactivos, y la primera actualización puede ser muy lenta.
FreshRSS admite la API Google Reader y la API Fever. La documentación oficial también indica que para móviles se recomienda la API Google Reader, ya que es más completa.
Pasos en el panel de administración:
- Entrar en la configuración.
- Habilitar el acceso a la API en los ajustes relacionados con la autenticación.
- Establecer la contraseña de la API en el perfil de usuario.
- Abrir la página de comprobación de la API y confirmar que la configuración pasa.
Para móvil puedes elegir clientes compatibles con la API Google Reader, como Reeder, NetNewsWire, FeedMe, Read You, etc. Que la contraseña de la API sea distinta de la de inicio de sesión es bueno: las apps móviles usan la contraseña de la API, y no deberías rellenar tu contraseña principal en todas partes.
Una dirección de API habitual suele ser similar a:
https://rss.example.com/api/greader.php
Cada cliente lo rellena de forma algo distinta; algunos solo necesitan el dominio raíz, otros necesitan la ruta de la API. Si falla el inicio de sesión, abre primero la página de comprobación de la API en el panel de FreshRSS; te dirá directamente qué está mal en la configuración del servidor.
Muchos sitios de internet en chino no tienen RSS, o lo tienen muy mal hecho. En ese caso FreshRSS puede combinarse con RSSHub.
Pero hay un punto de seguridad que debes tener en cuenta: FreshRSS bloquea por defecto el acceso a algunas direcciones internas para evitar riesgos tipo SSRF. Si ejecutas RSSHub en la misma red Docker, debes configurar INTERNAL_HOST_ALLOWLIST según la documentación oficial de Docker, permitiendo solo servicios internos explícitos, por ejemplo:
environment:
INTERNAL_HOST_ALLOWLIST: rsshub:1200
No escribas * o 0.0.0.0/0 por comodidad. Eso abre el acceso a todas las direcciones internas y es muy arriesgado.
Si no despliegas RSSHub y te suscribes directamente a fuentes RSS públicas, no necesitas esta configuración.
Con la opción SQLite, la copia de seguridad principal es data y extensions. Pero las bases de datos SQLite no son adecuadas para empaquetarse en línea como única copia de seguridad mientras el servicio escribe continuamente. Un enfoque más fiable es: detener temporalmente el contenedor FreshRSS antes de la copia, completar el archivado de archivos y luego iniciarlo de nuevo.
Crear un script de copia de seguridad:
nano backup-freshrss.sh
Escribir:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/opt/freshrss"
BACKUP_DIR="$APP_DIR/backups"
DATE="$(date +%F-%H%M)"
mkdir -p "$BACKUP_DIR/$DATE"
cd "$APP_DIR"
# 先尝试生成 FreshRSS 自带数据库快照,便于迁移和细粒度恢复。
# 如果这一步失败,脚本会停止,避免产生“看似成功但实际不完整”的备份。
docker exec --user www-data freshrss cli/db-backup.php
# 停止 FreshRSS,避免在线打包 SQLite 数据库时出现不一致。
docker compose stop freshrss
# 备份配置、SQLite 数据、扩展、Compose 和 Caddy 配置。
tar czf "$BACKUP_DIR/$DATE/freshrss-data.tar.gz" data extensions docker-compose.yml Caddyfile .env
# 重新启动服务。
docker compose start freshrss
# 保留最近 14 天备份。
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
echo "Backup saved to $BACKUP_DIR/$DATE"
Añadir permiso de ejecución y probar:
chmod +x backup-freshrss.sh
./backup-freshrss.sh
ls -lh backups
Añadir una tarea programada:
crontab -e
Copia de seguridad a las 3 de la madrugada cada día:
0 3 * * * /opt/freshrss/backup-freshrss.sh >> /opt/freshrss/backups/backup.log 2>&1
Este script detiene brevemente FreshRSS, lo cual suele ser aceptable para un lector personal. Si no puedes aceptar el tiempo de inactividad, deberías usar la exportación db-backup.php integrada de FreshRSS como ruta principal de copia de seguridad de la base de datos, hacer copia de seguridad de la configuración y las extensiones por separado, y no tratar un tar en línea del archivo SQLite en ejecución como única base de recuperación.
La copia de seguridad local es solo la primera capa. Un enfoque más fiable es usar rclone para sincronizar con almacenamiento de objetos, otro VPS o un NAS.
Detener el servicio antes de restaurar:
cd /opt/freshrss
docker compose down
Encontrar la copia de seguridad a restaurar, por ejemplo:
ls -lh /opt/freshrss/backups
Suponiendo que el directorio de copia de seguridad es /opt/freshrss/backups/2026-07-09-0300/, extraer:
tar xzf /opt/freshrss/backups/2026-07-09-0300/freshrss-data.tar.gz -C /opt/freshrss
Si lo que restauras es una instantánea de base de datos generada por cli/db-backup.php, también puedes usar cli/db-restore.php según el proceso oficial de restauración de FreshRSS. No mezcles sin cuidado la restauración a nivel de archivos y la restauración a nivel de base de datos; primero confirma si estás restaurando el directorio completo del sitio o una exportación de la base de datos.
Corregir los permisos del bind mount:
cd /opt/freshrss
sudo chown -R 33:33 data extensions
Reiniciar:
docker compose up -d
Después de restaurar, comprobar:
- Si puedes iniciar sesión.
- Si las fuentes siguen ahí.
- Si los estados de lectura son correctos.
- Si la API móvil sigue conectando.
- Si la actualización automática se ejecuta.
No te limites a hacer copias de seguridad sin ensayar. Los datos RSS no son necesariamente un negocio crítico, pero perder años de suscripciones y artículos guardados también duele.
Hacer copia de seguridad antes de actualizar:
cd /opt/freshrss
./backup-freshrss.sh
Si usas Docker Compose:
docker compose pull
docker compose up -d
Comprobar después de actualizar:
docker compose ps
docker compose logs --tail=100 freshrss
La documentación oficial de actualización advierte: para actualizaciones entre versiones es mejor ir versión por versión, no saltar directamente de una versión muy antigua a una muy nueva. Para instancias sin mantenimiento durante mucho tiempo, se recomienda revisar primero las Release Notes y luego decidir la ruta de actualización.
Como mínimo, haz esto:
- Abrir solo 80/443, no abrir 8080.
- Usar una contraseña de administrador fuerte.
- Configurar la contraseña de la API por separado de la de inicio de sesión.
- No poner
data,extensionsnibackupsen directorios accesibles desde la web. - No configurar
INTERNAL_HOST_ALLOWLIST=*a la ligera. - Si no necesitas registro público, no habilites el registro multiusuario.
- Actualizar periódicamente la imagen y los parches del sistema del VPS.
- Sincronizar las copias de seguridad fuera del sitio.
- Si solo lo usas tú, puedes añadir además Cloudflare Access, Tailscale o WireGuard.
FreshRSS en sí no es pesado, pero obtiene URL externas. Todo servicio que pueda obtener páginas web externas debe prestar atención a SSRF y a los límites de acceso a la red interna.
Primero revisa el contenedor:
docker compose ps
Luego revisa Caddy:
docker compose logs --tail=100 caddy
Céntrate en DNS, 80/443, firewall y grupos de seguridad. En modo proxy de Cloudflare, una configuración incorrecta del certificado o del origen también puede causar problemas de acceso.
Comprobar si CRON_MIN existe en .env:
grep CRON_MIN .env
Luego revisar los registros:
docker compose logs --tail=200 freshrss
Si eliges el cron del host, no configures CRON_MIN de nuevo, para evitar que dos mecanismos de actualización se ejecuten a la vez.
Comprobar en orden:
- Si el acceso a la API está habilitado en el panel de administración.
- Si la contraseña de la API está configurada en el perfil de usuario.
- Si el cliente ha seleccionado la API Google Reader.
- Si la dirección de la API está bien rellenada.
- Si el proxy inverso reenvía correctamente la ruta.
La página de comprobación de la API del panel de FreshRSS es muy útil; mira primero su PASS/FAIL y no cambies el cliente a ciegas.
Esto es algo que FreshRSS no puede resolver. FreshRSS es un lector, no un scraper universal.
Soluciones:
- Buscar si el sitio tiene un RSS oculto.
- Usar RSSHub.
- Usar RSS-Bridge.
- Usar la función de scraping web de FreshRSS para páginas simples.
Pero cuanto más se parezca a scraping, más hay que prestar atención a la frecuencia, los derechos de autor y las reglas del sitio objetivo.
Las causas comunes son la caché, las imágenes, las extensiones o una gran cantidad de artículos históricos.
Primero comprobar el tamaño de los directorios:
du -h --max-depth=2 /opt/freshrss | sort -h
La documentación oficial de copias de seguridad menciona que cache se puede omitir, porque FreshRSS puede reconstruir la caché. También puedes ajustar la política de retención de artículos históricos para evitar un crecimiento ilimitado.
Estas herramientas se confunden a menudo, pero tienen propósitos distintos.
| Herramienta | Propósito principal |
|---|---|
| FreshRSS | Lectura RSS, suscripciones, sincronización de estados de lectura |
| RSSHub | Convertir sitios sin RSS en fuentes RSS |
| Linkwarden | Gestión de marcadores y archivo web |
| Wallabag | Lectura posterior y guardado de artículos |
Si tu objetivo es leer fuentes cada día, elige FreshRSS. Si tu objetivo es guardar páginas web y conservar instantáneas, elige Linkwarden. Si quieres suscribirte a fuentes complejas como Zhihu, Weibo o Bilibili, FreshRSS puede combinarse con RSSHub.
Comprobar antes del uso formal:
- El dominio ya resuelve al VPS.
- 80/443 están permitidos en el firewall y los grupos de seguridad.
- FreshRSS está vinculado solo a
127.0.0.1:8080. - Caddy ha obtenido correctamente el certificado HTTPS.
-
TZ=Asia/Shanghaiestá configurado. -
CRON_MINestá configurado y solo hay un método de actualización habilitado. -
datayextensionsestán concedidos al usuariowww-datadentro del contenedor, o se han cambiado a volúmenes con nombre. - La contraseña de administrador está configurada como contraseña fuerte.
- La API móvil está habilitada y probada correctamente.
- El script de copia de seguridad se ha ejecutado correctamente.
- Se ha hecho al menos un ensayo de restauración.
- Si se usa RSSHub,
INTERNAL_HOST_ALLOWLISTsolo permite servicios explícitos.
El valor de FreshRSS no está en presumir de técnica, sino en convertir el flujo de información de nuevo en algo que puedes controlar.
Puedes poner en FreshRSS blogs técnicos, Releases de proyectos de código abierto, sitios de noticias, actualizaciones de pódcasts y publicaciones de foros. Abre el lector cada día y lee solo lo que has suscrito, sin dejarte llevar por un flujo de recomendaciones.
Si ya tienes un VPS, FreshRSS es una aplicación ligera que merece mucho la pena autoalojar. No consume recursos, las copias de seguridad son sencillas, la sincronización móvil es madura y, combinado con RSSHub, también encaja muy bien en escenarios de suscripción de información de internet en chino.