Tutoriales de VPS
Montar Nextcloud en un VPS (2026): Docker, PostgreSQL, Redis, HTTPS con Caddy y sincronización móvil
Monta una nube privada Nextcloud en un VPS desde cero: ejecuta Nextcloud, PostgreSQL, Redis y Cron con Docker Compose, configura HTTPS con Caddy y gestiona por completo la subida de archivos grandes, la sincronización móvil, WebDAV, la copia de seguridad y restauración, las actualizaciones y el endurecimiento de seguridad.

Si quieres guardar fotos, documentos y archivos de equipo en un servidor que tú controlas, y además necesitas acceso web, enlaces para compartir, historial de versiones y subida automática desde el móvil, Nextcloud es una solución de nube privada en VPS con un volumen de búsqueda y una demanda de uso muy altos. Es más completa que un simple listado de archivos WebDAV, pero también depende más que Syncthing u OpenList de la base de datos, la caché, las tareas programadas y una configuración correcta del proxy inverso.
Este artículo usa Ubuntu 24.04 LTS, Nextcloud 33, PostgreSQL, Redis, Docker Compose y Caddy como ejemplo para desplegar desde cero una nube privada de un solo servidor que se pueda mantener a largo plazo. La aplicación solo escucha en el puerto local del VPS, la base de datos y Redis no se exponen a Internet, y se incluyen la subida de archivos grandes, Cron, la sincronización de escritorio y móvil, la copia de seguridad y restauración, las actualizaciones y el tratamiento de alertas habituales.
Las tres herramientas permiten acceder a archivos, pero resuelven problemas distintos:
| Herramienta | Necesidades para las que encaja mejor | Principales contrapartidas |
|---|---|---|
| Nextcloud | Nube centralizada, usuarios, uso compartido, versiones, calendario y colaboración | Completa, con mayor coste de recursos y mantenimiento |
| Syncthing | Sincronización de carpetas punto a punto entre varios dispositivos | Sincronización eficiente, pero no es un portal de nube completo |
| OpenList | Agregar nubes, WebDAV y un punto de entrada unificado a los archivos | Ligera y cómoda, no debe ser la única copia de los datos |
Si solo quieres sincronizar archivos entre un ordenador y un NAS, puedes empezar por el tutorial de Syncthing en VPS. Si tu prioridad es montar varias nubes de terceros, encaja mejor el tutorial de OpenList en VPS. Nextcloud es más adecuado para personas o equipos pequeños que necesitan cuentas, permisos, uso compartido, historial de versiones y copia de seguridad de fotos desde el móvil.
La presión de CPU de Nextcloud suele venir de las miniaturas, la búsqueda de texto completo, las actualizaciones de aplicaciones y la concurrencia de varios usuarios, mientras que la presión de disco viene de una gran cantidad de archivos pequeños, el historial de versiones y la papelera.
| Escala de uso | Configuración recomendada | Notas |
|---|---|---|
| 1-3 personas, pocos documentos y fotos | 2 núcleos, 4 GB, 100 GB SSD | Puede ejecutar sincronización y uso compartido básicos |
| 3-10 personas, copia de seguridad de fotos del móvil | 4 núcleos, 8 GB, 250 GB SSD o más | Deja margen para la generación de vistas previas, la base de datos y Redis |
| Grandes volúmenes de fotos y vídeos o colaboración en equipo | 4-8 núcleos, 8-16 GB, disco de datos independiente | Requiere supervisar capacidad, IOPS y ventanas de copia de seguridad |
El disco del sistema y el disco de datos pueden separarse, pero no coloques el directorio de datos de Nextcloud directamente en un montaje de red inestable. El almacenamiento de objetos tampoco es más rápido por defecto: encaja en escenarios que necesitan claramente ampliar capacidad y desacoplar el ciclo de vida del host, mientras que un sitio pequeño de un solo servidor debe priorizar SSD/NVMe locales fiables y después hacer copias de seguridad externas. Consulta Cuándo necesita un VPS almacenamiento de objetos para valorar si merece la pena añadir complejidad.
Prepara un dominio, por ejemplo cloud.example.com, y apunta los registros A / AAAA al VPS. Abre a Internet solo SSH, 80 y 443; no abras PostgreSQL 5432, Redis 6379 ni el puerto local 8080 de Nextcloud.
Instala Docker Engine y Compose v2 desde el repositorio oficial de Docker y luego confirma las versiones:
docker --version
docker compose version
Crea los directorios de despliegue, configuración y copias de seguridad:
sudo mkdir -p /opt/nextcloud/backups
sudo chown -R "$USER":"$USER" /opt/nextcloud
cd /opt/nextcloud
Genera las contraseñas de PostgreSQL, Redis y el administrador de Nextcloud:
openssl rand -hex 32
openssl rand -hex 32
openssl rand -base64 32
nano .env
chmod 600 .env
Escribe /opt/nextcloud/.env:
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD=替换为第一组随机值
REDIS_HOST_PASSWORD=替换为第二组随机值
NEXTCLOUD_ADMIN_USER=ncadmin
NEXTCLOUD_ADMIN_PASSWORD=替换为第三组随机值
NEXTCLOUD_HOST=cloud.example.com
NEXTCLOUD_URL=https://cloud.example.com
PHP_MEMORY_LIMIT=1024M
PHP_UPLOAD_LIMIT=10G
No uses admin como cuenta de administrador y no reutilices la contraseña del VPS, del correo o de la base de datos. Tras la primera inicialización debes seguir conservando .env, pero debes restringir los permisos de lectura e incluirlo en copias de seguridad cifradas.
Crea /opt/nextcloud/compose.yaml:
services:
db:
image: postgres:17-alpine
container_name: nextcloud-db
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_DB: "${POSTGRES_DB:?set POSTGRES_DB in .env}"
POSTGRES_USER: "${POSTGRES_USER:?set POSTGRES_USER in .env}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}"
volumes:
- nextcloud_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 20
start_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
redis:
image: redis:8-alpine
container_name: nextcloud-redis
restart: unless-stopped
env_file:
- .env
command:
- sh
- -c
- exec redis-server --appendonly yes --requirepass "$$REDIS_HOST_PASSWORD"
volumes:
- nextcloud_redis:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli -a $$REDIS_HOST_PASSWORD ping | grep -q PONG"]
interval: 10s
timeout: 5s
retries: 20
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
app:
image: nextcloud:33-apache
container_name: nextcloud
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "127.0.0.1:8080:80"
env_file:
- .env
environment:
POSTGRES_HOST: db
POSTGRES_DB: "${POSTGRES_DB:?set POSTGRES_DB in .env}"
POSTGRES_USER: "${POSTGRES_USER:?set POSTGRES_USER in .env}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}"
REDIS_HOST: redis
REDIS_HOST_PASSWORD: "${REDIS_HOST_PASSWORD:?set REDIS_HOST_PASSWORD in .env}"
NEXTCLOUD_ADMIN_USER: "${NEXTCLOUD_ADMIN_USER:?set NEXTCLOUD_ADMIN_USER in .env}"
NEXTCLOUD_ADMIN_PASSWORD: "${NEXTCLOUD_ADMIN_PASSWORD:?set NEXTCLOUD_ADMIN_PASSWORD in .env}"
NEXTCLOUD_TRUSTED_DOMAINS: "${NEXTCLOUD_HOST:?set NEXTCLOUD_HOST in .env}"
TRUSTED_PROXIES: "172.16.0.0/12"
OVERWRITEHOST: "${NEXTCLOUD_HOST:?set NEXTCLOUD_HOST in .env}"
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: "${NEXTCLOUD_URL:?set NEXTCLOUD_URL in .env}"
PHP_MEMORY_LIMIT: "${PHP_MEMORY_LIMIT:-1024M}"
PHP_UPLOAD_LIMIT: "${PHP_UPLOAD_LIMIT:-10G}"
APACHE_BODY_LIMIT: 0
volumes:
- nextcloud_data:/var/www/html
healthcheck:
test: ["CMD-SHELL", "php -r '$$c=@fsockopen(\"127.0.0.1\",80,$$e,$$s,5); exit($$c ? 0 : 1);'"]
interval: 30s
timeout: 10s
retries: 10
start_period: 120s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
cron:
image: nextcloud:33-apache
container_name: nextcloud-cron
restart: unless-stopped
entrypoint: /cron.sh
depends_on:
app:
condition: service_healthy
env_file:
- .env
volumes:
- nextcloud_data:/var/www/html
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
nextcloud_db:
name: nextcloud_db
nextcloud_redis:
name: nextcloud_redis
nextcloud_data:
name: nextcloud_data
Nextcloud solo escucha en 127.0.0.1:8080. PostgreSQL y Redis no tienen mapeo de puertos y solo se pueden alcanzar desde la red de Compose. TRUSTED_PROXIES solo debe confiar en la dirección de red privada de Docker; no introduzcas 0.0.0.0/0. Si tu pool de direcciones de Docker es distinto, primero usa docker network inspect para averiguar la subred real y luego modifícala.
Comprueba el resultado expandido de Compose antes de iniciar:
docker compose config >/dev/null
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=150 app db redis
La primera inicialización puede tardar unos minutos. Cuando el servicio esté estable, comprueba el punto de entrada local:
curl -I http://127.0.0.1:8080/status.php
sudo ss -lntp | grep ':8080'
La dirección de escucha debe ser 127.0.0.1:8080, no 0.0.0.0:8080.
Edita /etc/caddy/Caddyfile:
cloud.example.com {
encode zstd gzip
request_body {
max_size 10GB
}
reverse_proxy 127.0.0.1:8080 {
transport http {
read_timeout 1h
write_timeout 1h
}
}
}
Valida y recarga:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl -I https://cloud.example.com/status.php
Caddy pasa X-Forwarded-For, X-Forwarded-Host y X-Forwarded-Proto. Los proxies de confianza y los parámetros de sobrescritura de Compose permiten que Nextcloud genere enlaces HTTPS correctamente, evitando bucles de inicio de sesión, direcciones WebDAV incorrectas y la generación de URL HTTP desde la línea de comandos. Para problemas más complejos de CDN, múltiples dominios y certificados, consulta la guía completa de proxy inverso con Caddy en VPS.
La subida de archivos grandes se ve afectada a la vez por Caddy, PHP, Apache, los tiempos de espera del cliente y el disco disponible. Establecer el límite en 10 GB no significa que se vayan a usar 10 GB de memoria de una vez, pero el disco debe tener espacio para los archivos temporales de subida, el historial de versiones y la papelera.
Abre https://cloud.example.com e inicia sesión con la cuenta de administrador de .env. Como Compose ya pasa las variables de base de datos y de administrador, la instalación suele completarse automáticamente en el primer arranque.
Ve a «Configuración de administración → Información general» y revisa una por una las advertencias de seguridad y configuración. Después ejecuta una comprobación de estado desde dentro del contenedor:
docker compose exec -u www-data app php occ status
docker compose exec -u www-data app php occ config:system:get trusted_domains
docker compose exec -u www-data app php occ config:system:get trusted_proxies
docker compose exec -u www-data app php occ config:system:get overwriteprotocol
No desactives las comprobaciones de seguridad solo para que desaparezcan las advertencias. Las advertencias de dominio, proxy, caché, tareas en segundo plano o índices de base de datos deben corregirse según su causa real.
El escaneo de archivos, la limpieza de versiones, las notificaciones y las tareas de aplicaciones de Nextcloud dependen de los trabajos en segundo plano. El modo AJAX predeterminado solo se activa cuando un usuario visita una página y no es adecuado para producción.
Compose ya ejecuta un contenedor cron independiente, así que configura el modo de tareas en segundo plano como Cron:
docker compose exec -u www-data app php occ background:cron
docker compose logs --tail=100 cron
Más adelante, confirma en la configuración de administración que la hora de «última ejecución de tarea» se sigue actualizando. El contenedor Cron y la App deben compartir el mismo volumen nextcloud_data; de lo contrario, la configuración y las aplicaciones instaladas serán inconsistentes.
La imagen oficial configura Redis según REDIS_HOST, pero tras el lanzamiento debes comprobarlo igualmente:
docker compose exec -u www-data app php occ config:system:get redis
docker compose exec redis sh -c 'redis-cli -a "$REDIS_HOST_PASSWORD" ping'
Redis se usa para la caché distribuida y el bloqueo transaccional de archivos, lo que reduce la contención de bloqueos de base de datos durante la sincronización de varios usuarios. El volumen de datos de Redis no sustituye a los datos de archivos de Nextcloud; aunque se pierda Redis, los archivos principales y la base de datos deben poder recuperarse, pero los bloqueos y la caché en curso se reconstruirán.
Instala el cliente de escritorio oficial de Nextcloud o el cliente de iOS/Android e introduce esta dirección de servidor:
https://cloud.example.com
Para la subida automática desde el móvil se recomienda crear primero un directorio dedicado, por ejemplo Photos/Phone, y decidir claramente si se sube solo con Wi-Fi y mientras se carga. Antes de la primera sincronización de muchas fotos, comprueba el espacio libre del VPS, la cuota de tráfico y la política de retención de versiones.
La dirección WebDAV suele ser:
https://cloud.example.com/remote.php/dav/files/用户名/
Tras activar la autenticación de dos factores, crea una contraseña de aplicación para los clientes WebDAV que no admiten inicio de sesión web y no guardes la contraseña principal en software antiguo.
Las versiones de archivos y la papelera aumentan notablemente el uso de disco. No esperes a que el disco esté lleno para ocuparte de ello. Consulta el espacio actual y el estado en segundo plano:
df -h
docker system df
docker compose exec -u www-data app php occ status
En instancias con muchas fotos, la generación de vistas previas consume CPU y disco. Activa solo los tipos de vista previa que realmente necesites y genéralas de antemano en horas de baja carga. No instales scripts de miniaturas de origen desconocido ni permitas que varios programas modifiquen directamente el directorio de datos de Nextcloud.
Si programas externos escriben en el directorio de datos, la base de datos de Nextcloud no conocerá automáticamente esos archivos. Debes usar una aplicación de almacenamiento externo compatible o ejecutar con cuidado:
docker compose exec -u www-data app php occ files:scan --all
Un escaneo completo puede tardar mucho con muchos archivos y no debe usarse con frecuencia como método de sincronización.
Una copia de seguridad completa incluye al menos PostgreSQL y el volumen nextcloud_data. La base de datos guarda usuarios, uso compartido, índice de archivos y estado de las aplicaciones; el volumen de datos guarda config.php, aplicaciones, temas y los archivos reales.
Primero activa el modo de mantenimiento para evitar escrituras continuas durante la copia de seguridad:
cd /opt/nextcloud
docker compose exec -u www-data app php occ maintenance:mode --on
Copia de seguridad de PostgreSQL:
mkdir -p backups
docker compose exec -T db sh -c \
'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --format=custom' \
> "backups/nextcloud-db-$(date +%F-%H%M).dump"
Copia de seguridad del volumen de datos:
docker run --rm \
-v nextcloud_data:/source:ro \
-v "$PWD/backups:/backup" \
alpine sh -c 'tar -C /source -czf /backup/nextcloud-data.tar.gz .'
Desactiva el modo de mantenimiento:
docker compose exec -u www-data app php occ maintenance:mode --off
Las copias de seguridad no deben quedarse solo en el mismo VPS. Cópialas cifradas a almacenamiento de objetos u otro servidor y realiza simulacros de restauración con regularidad. Puedes seguir directamente el proceso de verificación externa del tutorial de simulacro de copia de seguridad y restauración en VPS.
Al restaurar, primero detén la App y Cron, restaura el volumen de datos y la base de datos, y luego inicia los servicios y ejecuta:
docker compose exec -u www-data app php occ maintenance:repair
docker compose exec -u www-data app php occ db:add-missing-indices
docker compose exec -u www-data app php occ status
La aceptación de la restauración debe comprobar el inicio de sesión, la descarga de archivos, los enlaces para compartir, el historial de versiones, la subida desde el móvil y WebDAV, no solo la página de inicio.
Nextcloud solo se puede actualizar siguiendo rutas de versión mayor compatibles; no sustituyas la imagen directamente saltando varias versiones mayores. Antes de actualizar, consulta las notas de la versión oficial, la compatibilidad de las aplicaciones y el disco disponible, y completa las copias de seguridad de la base de datos y del volumen de datos.
Actualización dentro de la misma versión mayor:
cd /opt/nextcloud
docker compose config >/dev/null
docker compose pull
docker compose up -d
docker compose exec -u www-data app php occ upgrade
docker compose exec -u www-data app php occ status
docker compose logs --tail=200 app cron
Tras la actualización, comprueba el resumen de administración, las tareas en segundo plano, la sincronización de clientes, los enlaces para compartir y las advertencias de base de datos. Si la nueva versión falla, detén las escrituras y restaura la etiqueta de imagen anterior, la base de datos y el volumen de datos. Cambiar solo la etiqueta de imagen sin restaurar la base de datos ya actualizada normalmente no permite una reversión fiable.
- Usa una contraseña larga para el administrador y activa la autenticación de dos factores;
- Agrupa a los usuarios normales con el mínimo privilegio y no compartas la cuenta de administrador;
- No expongas PostgreSQL, Redis ni 8080 a Internet;
- Instala solo aplicaciones de Nextcloud con mantenimiento activo y que realmente necesites;
- Actualiza periódicamente Nextcloud, la base de datos, Redis, Caddy y el sistema anfitrión;
- Establece límites de velocidad razonables en el inicio de sesión, pero sin bloquear por error WebDAV ni los clientes de sincronización;
- Supervisa el disco, los inodos, las copias de seguridad de la base de datos, Cron y HTTPS;
- Revoca sesiones y contraseñas de aplicación cuando alguien deje el equipo o se pierda un dispositivo.
El autoalojamiento significa que tú eres responsable de la seguridad, la capacidad y la recuperación; no significa que los archivos estén libres de pérdida por naturaleza. RAID, las instantáneas, el historial de versiones y la papelera no sustituyen a las copias de seguridad externas.
docker compose ps
curl -I http://127.0.0.1:8080/status.php
docker compose logs --tail=200 app
sudo journalctl -u caddy --no-pager -n 100
Si la App aún se está inicializando o ejecutando una actualización, Caddy devolverá temporalmente 502. Si sigue fallando, comprueba la base de datos, Redis, la memoria y los permisos del volumen de datos.
Confirma que el dominio en .env coincide con el punto de entrada real y comprueba:
docker compose exec -u www-data app php occ config:system:get trusted_domains
docker compose exec -u www-data app php occ config:system:get trusted_proxies
docker compose exec -u www-data app php occ config:system:get overwritehost
docker compose exec -u www-data app php occ config:system:get overwriteprotocol
No configures redirecciones HTTP/HTTPS conflictivas entre el CDN, Caddy y Nextcloud. Los proxies de confianza tampoco deben relajarse a direcciones públicas arbitrarias.
Primero confirma el max_size de Caddy y luego revisa el entorno del contenedor y PHP:
docker compose exec app php -i | grep -E 'upload_max_filesize|post_max_size|memory_limit'
docker compose logs --tail=200 app
df -h
Si la subida falla a un tamaño fijo, suele ser un límite del proxy o de PHP; si se agota el tiempo a mitad, sigue revisando el cliente, los tiempos de espera del proxy inverso, el disco y la estabilidad de la red.
La página de administración indica que las tareas en segundo plano llevan mucho tiempo sin ejecutarse
docker compose ps cron
docker compose logs --tail=200 cron
docker compose exec -u www-data app php occ background:cron
Confirma que Cron y la App usan la misma versión de imagen y el mismo volumen de datos. Tras actualizar la App, actualiza también el contenedor Cron.
Primero comprueba si Redis y la base de datos están sanos; no borres directamente los registros de bloqueo de la base de datos:
docker compose ps redis db
docker compose logs --tail=200 redis db app
docker compose exec redis sh -c 'redis-cli -a "$REDIS_HOST_PASSWORD" ping'
Solo trata más a fondo los bloqueos residuales cuando hayas confirmado que ningún cliente está escribiendo, hayas hecho copia de seguridad de la base de datos y entiendas el impacto.
- Nextcloud solo escucha en
127.0.0.1:8080, y la base de datos y Redis no están expuestos a Internet - El dominio, HTTPS, los proxies de confianza y la URL de sobrescritura están configurados de forma coherente
- El contenedor Cron independiente funciona correctamente y la hora de las tareas en segundo plano se sigue actualizando
- La caché de Redis y el bloqueo transaccional de archivos funcionan correctamente
- Se han probado el cliente de escritorio, la subida automática desde el móvil y WebDAV
- Se ha probado realmente la ruta de subida de 10 GB o del tamaño necesario
- Tanto PostgreSQL como el volumen de datos de Nextcloud tienen copias de seguridad externas
- Se ha realizado un simulacro de restauración y verificado el uso compartido, las versiones y la sincronización de clientes
- Antes de actualizar se fija la versión y se hace copia de seguridad, y después se comprueba la compatibilidad de las aplicaciones
Tras completar estos pasos, este Nextcloud no será solo «una página web que se abre», sino una nube privada en VPS con HTTPS, caché, tareas programadas, sincronización móvil, gestión de versiones y capacidad de recuperación.