Instalar Nextcloud en tu propio servidor

Publicado el 15 min de lectura

Del servidor vacío a una página de resumen sin avisos: servidor web, módulos de PHP, base de datos, permisos del directorio de datos, trusted_domains, límites de subida y tareas en segundo plano por cron.

Nextcloud se descomprime rápido. La parte que cuesta tiempo viene después: en una instalación recién hecha, la página de resumen que hay bajo Ajustes de administración muestra casi siempre una lista de avisos amarillos y rojos, las subidas se cortan a los 2 MB y el acceso por dirección IP acaba con "You are accessing the site from an untrusted domain.". Este artículo recorre el camino completo y se detiene justo en los puntos donde la mayoría de las guías se quedan.

Decidir antes: versión de PHP, base de datos y ubicación de los datos

Nextcloud depende de la versión de PHP mucho más que casi cualquier otro software de servidor. Las series actuales 33 y 34 exigen como mínimo PHP 8.2, y la serie 32 todavía arranca a partir de PHP 8.1. Por eso es la distribución la que decide si te basta con lo que trae de serie:

SistemaPHP de la distribuciónBase de datos de la distribuciónValoración
Debian 13 (trixie)8.4MariaDB 11.8sirve sin repositorio externo
Debian 12 (bookworm)8.2MariaDB 10.11sirve, pero justo en el límite
Ubuntu 24.04 LTS8.3MariaDB 10.11, MySQL 8.0sirve sin repositorio externo
Ubuntu 22.04 LTS8.1MariaDB 10.6, MySQL 8.0demasiado antiguo para Nextcloud 33 y 34
Debian 11 (bullseye)7.4MariaDB 10.5queda descartado

Debian 11 es el caso más duro y, aun así, suele notarse tarde, porque la instalación de los paquetes termina sin ningún error. Allí el metapaquete php arrastra PHP 7.4, y la versión actual de Nextcloud se detiene con eso en la primera llamada desde el navegador con un HTTP 500 y el mensaje "This version of Nextcloud requires at least PHP 8.2". Debian 11 ya ha salido además del soporte regular, así que para una instalación nueva es la base equivocada. Si no te queda otro remedio, añade antes el repositorio Sury e instala expresamente paquetes con versión, es decir php8.2-fpm, php8.2-cli, php8.2-mysql y así sucesivamente, en lugar de los metapaquetes sin versión.

En Ubuntu 22.04 te estrellas de forma igual de inevitable en cuanto instalas la versión actual de Nextcloud. O te quedas conscientemente en la serie 32, o traes PHP desde el conocido PPA:

sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get update
sudo apt-get install -y php8.3-fpm php8.3-cli php8.3-mysql

Otros dos puntos que se fijan al principio y que luego solo se cambian a base de esfuerzo: Debian no incluye por norma ningún paquete mysql-server, allí MariaDB es lo establecido. Y el directorio de datos no va bajo /var/www/nextcloud/data, sino fuera de la raíz del servidor web, por ejemplo en /var/nextcloud-data. La ruta predeterminada solo resulta peligrosa porque una configuración rota del servidor web serviría entonces todos los archivos de los usuarios. Nextcloud avisa en ese caso con "Your data directory and files are probably accessible from the internet", pero justo después de que el fallo ya exista.

Configurar el servidor web, PHP y la base de datos

Nosotros usamos nginx con PHP-FPM. Si prefieres Apache con mod_php, el camino está descrito en Apache, PHP y MySQL en Debian, y los temas de PHP de más abajo se aplican sin cambios. Una instalación básica de nginx la encuentras en instalar nginx.

sudo apt-get update
sudo apt-get install -y nginx mariadb-server
sudo apt-get install -y php-fpm php-cli php-mysql php-gd php-curl php-mbstring php-intl php-gmp php-bcmath php-xml php-zip php-imagick php-apcu

Esta lista es deliberadamente más larga que el mínimo. bcmath y gmp los necesita Nextcloud para el inicio de sesión sin contraseña, intl para ordenar correctamente los acentos y los caracteres especiales, imagick para las miniaturas y apcu para la caché local. Si falta alguno de los módulos obligatorios, no pasas siquiera del asistente de instalación: la página enumera entonces por su nombre los módulos que faltan.

Comprueba después qué se ha cargado realmente:

php -v
php -m

El segundo tropiezo habitual: hay dos configuraciones de PHP separadas, una para la línea de comandos y otra para FPM. php --ini te muestra la de la línea de comandos, mientras que la del servidor web está en /etc/php/<version>/fpm/php.ini. Los cambios en el archivo equivocado no surten ningún efecto, y eso es lo que más tiempo cuesta según la experiencia.

Ahora la base de datos. Asegura primero MariaDB, mira asegurar MariaDB y MySQL, y después:

sudo mariadb -e "CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
sudo mariadb -e "CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'UnaClaveLarga';"
sudo mariadb -e "GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';"
sudo mariadb -e "FLUSH PRIVILEGES;"

El utf8mb4 del primer comando no es un detalle menor. Si creas la base de datos con utf8, Nextcloud avisa más tarde con "MySQL is used as database but does not support 4-byte characters", y hacer el cambio con el sistema en marcha resulta bastante más incómodo que poner el CREATE DATABASE correcto desde el principio. Si falla el inicio de sesión del usuario de base de datos, te ayuda solucionar Access denied for user.

Descomprimir y asignar permisos

sudo apt-get install -y wget unzip
wget https://download.nextcloud.com/server/releases/latest.zip
sudo unzip -q latest.zip -d /var/www
sudo mkdir -p /var/nextcloud-data
sudo chown -R www-data:www-data /var/www/nextcloud
sudo chown -R www-data:www-data /var/nextcloud-data
sudo chmod 750 /var/nextcloud-data

Los permisos son el punto en el que más a menudo se hace el trabajo a medias. Tres síntomas y su causa:

  • "Cannot write into config directory": /var/www/nextcloud/config no pertenece al usuario del servidor web. En Debian y Ubuntu ese usuario es www-data, mientras que en AlmaLinux y Rocky es apache o nginx. Un chown www-data copiado a ciegas no sirve de nada en la familia Red Hat.
  • "Can't create or write into the data directory": la ruta no existe, no es una ruta absoluta, o algún directorio superior no es accesible para www-data.
  • "Your data directory is readable by other users": permisos demasiado amplios. Basta con un chmod 750 sobre el directorio de datos.

Resiste la tentación de aplastar el problema con chmod -R 777. Nextcloud te devuelve exactamente el aviso que querías quitarte de encima y, de paso, has dejado cada archivo legible para cualquier usuario local.

La configuración de nginx

Nextcloud necesita bastante más que un bloque PHP estándar, entre otras cosas las reescrituras para el descubrimiento de servicios bajo /.well-known/ y los bloqueos de los directorios internos. Lo siguiente es la versión abreviada de la plantilla oficial y funciona tal cual:

upstream php-handler {
    server unix:/run/php/php8.3-fpm.sock;
}

server {
    listen 80;
    server_name cloud.example.com;
    root /var/www/nextcloud;

    client_max_body_size 10G;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;

    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "noindex, nofollow" always;

    index index.php index.html /index.php$request_uri;

    location ^~ /.well-known {
        location = /.well-known/carddav { return 301 /remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /remote.php/dav/; }
        location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
        return 301 /index.php$request_uri;
    }

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)              { return 404; }

    location ~ \.php(?:$|/) {
        rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+) /index.php$request_uri;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        set $path_info $fastcgi_path_info;
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_request_buffering off;
        fastcgi_max_temp_file_size 0;
    }

    location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|map|woff2)$ {
        try_files $uri /index.php$request_uri;
        expires 6M;
        access_log off;
    }

    location / {
        try_files $uri $uri/ /index.php$request_uri;
    }
}

La ruta al socket de FPM tienes que adaptarla a tu versión de PHP. En Debian 13 se llama php8.4-fpm.sock, en Debian 12 php8.2-fpm.sock, en Ubuntu 24.04 php8.3-fpm.sock y en Ubuntu 22.04 php8.1-fpm.sock. Si el nombre no coincide, obtienes un 502 Bad Gateway. El nombre real lo muestra:

systemctl enable --now php8.4-fpm
ls /run/php/

La primera línea forma parte del asunto, porque el archivo de socket no aparece hasta que arranca el servicio. Antes, /run/php/ está vacío o directamente no existe, y ls responde con No such file or directory. En un servidor normal el propio paquete arranca el servicio durante la instalación, pero después de una reinstalación o dentro de un container eso no está garantizado. Ajusta el número de versión del nombre del servicio a tu instalación. Después, nginx -t y recargar.

Instalación, HTTPS y trusted_domains

Puedes recorrer la instalación a golpe de clic en el navegador o resolverla directamente en la línea de comandos. La segunda variante es reproducible y se puede meter en un script:

cd /var/www/nextcloud
sudo -u www-data php occ maintenance:install --database "mysql" --database-name "nextcloud" --database-user "nextcloud" --database-pass "UnaClaveLarga" --admin-user "admin" --admin-pass "OtraClaveLarga" --data-dir "/var/nextcloud-data"

Dos trampas aquí. Primera, el comando tiene que ejecutarse desde el directorio de Nextcloud, si no PHP se detiene con un Fatal Error. Segunda, occ nunca debe ejecutarse como root, porque entonces aparece "Console has to be executed with the user that owns the file config/config.php" y, en el peor de los casos, los archivos recién creados acaban perteneciendo al usuario equivocado.

Ahora HTTPS. Sin certificado, las aplicaciones móviles se niegan a funcionar y Nextcloud avisa en la página de resumen:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.com

Para varios subdominios merece la pena un certificado wildcard. Añade después en el bloque de servidor TLS la cabecera Strict-Transport-Security "max-age=15552000; includeSubDomains" always;, porque si no se queda ahí el aviso "The Strict-Transport-Security HTTP header is not configured to at least 15552000 seconds".

El clásico para terminar: llamas a la página con un nombre distinto al que usaste en la instalación y solo ves "You are accessing the site from an untrusted domain." Nextcloud acepta exclusivamente los nombres de host que figuran en trusted_domains. Para añadirlo sin editar el archivo a mano:

sudo -u www-data php occ config:system:set trusted_domains 1 --value=cloud.example.com
sudo -u www-data php occ config:system:get trusted_domains

El índice empieza a contar en 0, y el 0 suele estar ya ocupado. Si asignas dos veces el mismo índice, sobrescribes la entrada existente y puede que acabes dejándote fuera a ti mismo. Si te pasa: config/config.php es un archivo PHP totalmente normal, y la entrada se puede corregir ahí con el editor. Define además overwrite.cli.url con la dirección HTTPS definitiva, porque si no las tareas en segundo plano generan enlaces con el nombre de host equivocado.

Tamaño de subida y límite de memoria

Los valores predeterminados de PHP se quedan cortos para Nextcloud. upload_max_filesize suele estar en 2M y memory_limit en 128M. Nextcloud recomienda al menos 512M de memoria y avisa en caso contrario con "The PHP memory limit is below the recommended value of 512MB".

Aquí hay tres sitios, y no basta con tocar uno de ellos:

  1. La configuración de FPM en /etc/php/<version>/fpm/php.ini: memory_limit = 512M, upload_max_filesize = 10G, post_max_size = 10G, max_execution_time = 3600. Después reinicia FPM, porque recargar nginx no basta.
  2. El archivo .user.ini del directorio de Nextcloud: Nextcloud trae sus propios valores y, como .user.ini se aplica por directorio, gana frente al php.ini global. Justo ahí encalla la búsqueda de errores una y otra vez. Ajusta también allí los valores. PHP guarda ese archivo en caché durante cinco minutos de forma predeterminada, así que tu cambio surte efecto con retraso.
  3. La directiva de nginx client_max_body_size: si falta o es demasiado baja, la subida se corta con "413 Request Entity Too Large" antes de que se llegue siquiera a preguntar a PHP.

Para comprobar qué llega al final, ayuda la página Ajustes de administración, donde figura el límite realmente efectivo. En la línea de comandos:

grep -E '^(memory_limit|upload_max_filesize|post_max_size)' /etc/php/8.4/fpm/php.ini

Consulta expresamente el archivo de FPM y no el de la línea de comandos. Un php -r "echo ini_get('memory_limit');" lee la SAPI de CLI, y esa devuelve -1 en todas las distribuciones comprobadas, es decir sin límite. Quien se fía de eso da el valor por suficiente y aun así choca más tarde con errores de memoria, porque en el archivo de FPM sigue estando memory_limit = 128M. Lo que realmente llega al navegador lo muestra php-fpm8.4 -i o un info.php depositado brevemente con phpinfo(), que borras enseguida después.

Si al subir archivos grandes el kernel termina el proceso, lo que falta sencillamente es RAM. Entonces ayuda configurar swap como remedio de urgencia, aunque más RAM es mejor.

Pasar las tareas en segundo plano a cron

Después de la instalación, Nextcloud funciona en modo AJAX: las tareas en segundo plano solo se ejecutan cuando alguien tiene la interfaz abierta. Ese es el motivo por el que la búsqueda de texto completo, las tareas de limpieza y las notificaciones parecen no ocurrir nunca en instancias poco usadas. Cambia a cron de verdad, Nextcloud espera una ejecución cada cinco minutos. Los fundamentos están en configurar un cron job en Linux.

sudo crontab -u www-data -e

Ahí se añade:

*/5 * * * * php -f /var/www/nextcloud/cron.php

Después hay que comunicarle a Nextcloud el cambio de modo:

cd /var/www/nextcloud
sudo -u www-data php occ background:cron

Quien prefiera trabajar sin demonio de cron, usa un servicio systemd con temporizador. La unidad nextcloudcron.service llama a /usr/bin/php -f /var/www/nextcloud/cron.php como usuario www-data, y el temporizador correspondiente define OnBootSec=5min y OnUnitActiveSec=5min.

Si aun así se mantiene el aviso "Last background job execution ran X hours ago. Something seems wrong", comprueba en este orden: ¿el trabajo se ejecuta con el usuario correcto? ¿La ruta existe de verdad? Y algo muy importante: ¿es cron.php ejecutable con la configuración de PHP de la línea de comandos, o falta allí un módulo que solo se instaló para FPM? La prueba más honesta es la llamada a mano, porque así ves cada mensaje de error en texto claro:

sudo -u www-data php -f /var/www/nextcloud/cron.php

Resolver los avisos de la página de resumen

La lista que hay bajo Ajustes de administración y Visión general no es un adorno, cada línea tiene un motivo concreto. Los avisos más frecuentes y su solución:

  • "No memory cache has been configured": APCu está instalado, pero no registrado. occ config:system:set memcache.local --value='\OC\Memcache\APCu'. Para que occ y las ejecuciones de cron también se beneficien, define además apc.enable_cli=1 en /etc/php/<version>/mods-available/apcu.ini.
  • "Transactional file locking is disabled": instala Redis (apt-get install -y redis-server php-redis) y pon memcache.locking en \OC\Memcache\Redis. En instancias de un solo usuario se puede prescindir de ello, pero en cuanto sincronizan varios usuarios a la vez, ya no.
  • "Your web server is not properly set up to resolve /.well-known/caldav": faltan las reescrituras del bloque location ^~ /.well-known. Se puede probar directamente con curl -I https://cloud.example.com/.well-known/caldav, y lo esperado es un 301 hacia /remote.php/dav/.
  • "Your installation has no default phone region set": occ config:system:set default_phone_region --value="AT", para España ES. El valor es un código de país según ISO 3166-1.
  • "Server has no maintenance window start time configured": occ config:system:set maintenance_window_start --type=integer --value=1. El valor es la hora de inicio en UTC, así los trabajos diarios costosos se ejecutan de noche y no en pleno horario de uso.
  • "The database is missing some indexes": occ db:add-missing-indices, y a juego con él occ db:add-missing-columns y occ db:add-missing-primary-keys. Estos comandos son lentos en instancias grandes, pero no entrañan ningún riesgo.
  • "PHP does not seem to be setup properly to query system environment variables": en el archivo de pool de FPM /etc/php/<version>/fpm/pool.d/www.conf hay que descomentar la línea env[PATH] = /usr/local/bin:/usr/bin:/bin y reiniciar FPM.
  • "Module php-imagick in this instance has no SVG support": esto no es un fallo de Nextcloud, sino una biblioteca delegate que falta en ImageMagick. Si no necesitas vistas previas de SVG, puedes dejar el aviso tal cual.

Cómo saber que de verdad funciona

Cuatro comprobaciones que, tomadas en conjunto, resultan concluyentes:

cd /var/www/nextcloud
sudo -u www-data php occ status
sudo -u www-data php occ check
sudo -u www-data php occ config:app:get core lastcron

occ status tiene que devolver installed: true y la versión esperada, y occ check no debe producir ninguna salida. El tercer comando devuelve una marca de tiempo Unix. Conviértela: no puede tener más de cinco minutos, y solo entonces tu cron está trabajando de verdad.

Desde fuera:

curl -s https://cloud.example.com/status.php

La respuesta es un objeto JSON con "installed":true, "maintenance":false y el número de versión. Si aquí llega HTML, alguna de tus reglas location abarca demasiado. Si llega una redirección a la página de inicio de sesión, todo está en orden, pero has usado la URL equivocada.

Por último, la prueba práctica que ninguna página de estado sustituye: subir un archivo de varios gigabytes por la interfaz web y sincronizarlo después con el cliente de escritorio. Solo así se ve si client_max_body_size, post_max_size, los timeouts y el espacio libre en disco encajan entre sí. Si de repente deja de funcionar todo, merece la pena echar un vistazo a los discos llenos, porque Nextcloud crea vistas previas y versiones que crecen de forma perceptible.

Cuando algo sale mal

Nextcloud escribe su registro en /var/nextcloud-data/nextcloud.log, es decir dentro de tu directorio de datos y no en /var/log. Ese es el primer archivo que deberías mirar, no el log de nginx. Para una salida legible:

sudo -u www-data php occ log:watch

Si la instancia se queda atascada en modo mantenimiento tras una actualización interrumpida, occ maintenance:mode --off la devuelve a la normalidad. Si la interfaz ya no es accesible en absoluto, pon 'maintenance' => false directamente en config/config.php.

Antes de tocar la base de datos o la configuración, haz un backup de las dos cosas. Un backup del directorio por sí solo no basta, porque Nextcloud no vale nada sin la base de datos que le corresponde:

sudo -u www-data php occ maintenance:mode --on
sudo mariadb-dump --single-transaction nextcloud > /root/nextcloud-db.sql
sudo -u www-data php occ maintenance:mode --off

Y un consejo de fondo: termina de montar el servidor antes de hacerlo accesible públicamente. Un firewall, un acceso SSH asegurado y los puntos de la lista de comprobación para servidores root nuevos van antes del primer inicio de sesión, no después. Una instancia de Nextcloud con la contraseña predeterminada se encuentra en cuestión de horas.

Preguntas frecuentes

¿Qué versión de PHP necesito para Nextcloud?
Nextcloud 33 y 34 exigen como mínimo PHP 8.2 y admiten hasta la 8.5, mientras que Nextcloud 32 arranca a partir de PHP 8.1. Debian 13 (PHP 8.4), Debian 12 (PHP 8.2) y Ubuntu 24.04 (PHP 8.3) encajan sin repositorio externo. Ubuntu 22.04 solo trae PHP 8.1 y resulta por tanto demasiado antiguo para las series actuales, así que allí necesitas el PPA ondrej/php. Debian 11 trae PHP 7.4: la instalación se completa sin errores, pero Nextcloud se detiene en la primera llamada con un HTTP 500 y "This version of Nextcloud requires at least PHP 8.2".
¿Por qué me aparece "You are accessing the site from an untrusted domain"?
Nextcloud solo acepta nombres de host que figuren en el array trusted_domains de config/config.php. Añade el nombre con sudo -u www-data php occ config:system:set trusted_domains 1 --value=cloud.example.com. El índice empieza a contar en 0, y un índice ya ocupado se sobrescribe.
¿Por qué se cortan las subidas pese a haber ampliado php.ini?
Hay tres límites. Además de upload_max_filesize y post_max_size en el php.ini de FPM, Nextcloud trae su propio .user.ini en el directorio de instalación, que gana porque se aplica por directorio, y nginx limita aparte mediante client_max_body_size. Si este último valor es demasiado bajo, aparece 413 Request Entity Too Large antes de que se llegue siquiera a preguntar a PHP.
¿Por qué no se ejecutan mis tareas en segundo plano?
Las instalaciones recién hechas están en modo AJAX, y ahí los trabajos solo se ejecutan mientras la interfaz está abierta. Añade */5 * * * * php -f /var/www/nextcloud/cron.php a la crontab de www-data y cambia el modo con occ background:cron. Lo puedes comprobar con occ config:app:get core lastcron, donde la marca de tiempo no debe tener más de cinco minutos.
¿Qué permisos necesita el directorio de datos?
Pertenece al usuario del servidor web (www-data en Debian y Ubuntu, apache o nginx en la familia Red Hat) y debería llevar un chmod 750. Unos permisos demasiado amplios provocan el mensaje "Your data directory is readable by other users". Crea el directorio fuera de la raíz del servidor web, por ejemplo en /var/nextcloud-data.
¿Cómo me quito el aviso sobre la caché de memoria que falta?
Instala php-apcu y define occ config:system:set memcache.local --value='\OC\Memcache\APCu'. Para que occ y las ejecuciones de cron usen también la caché, añade además apc.enable_cli=1 en el archivo apcu.ini de la línea de comandos de PHP.
¿Puedo usar Nextcloud también con PostgreSQL?
Sí, Nextcloud admite PostgreSQL en igualdad de condiciones y la documentación incluso lo menciona como opción preferente. En Debian eso resuelve de paso la cuestión de MySQL, que allí no está disponible como paquete de todas formas. Durante la instalación indicas --database "pgsql" en lugar de "mysql".

Nextcloud Autoalojamiento PHP nginx MariaDB Debian Ubuntu Cloud Linux