How to install NextCloud 32 on Debian 12 with Nginx & MariaDB
 
  		This guide assumes a fresh Debian 12 server. If not running as root, prefix your commands with sudo.
1. Install dependencies.
apt update apt -y install nginx mariadb-server php-fpm php-common php-gd php-curl php-zip php-xml php-mbstring php-intl php-imap php-bcmath php-redis php-mysql php-gmp php-apcu php-imagick redis imagemagick memcached
Run the MariaDB initial setup wizard:
mysql_secure_installation
2. Add a SQL database and user for Nextcloud
mysql -u root -p CREATE DATABASE nextcloud; CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'Your_Own_Strong_Password_Here'; GRANT ALL ON nextcloud.* TO 'nextcloud'@'localhost' IDENTIFIED BY 'Your_Own_Strong_Password_Here'; FLUSH PRIVILEGES; exit
3. Download the Nextcloud installer, unzip the archive and set ownership.
cd /var/www wget https://download.nextcloud.com/server/releases/latest.zip apt -y install unzip unzip latest.zip chown -R www-data:www-data nextcloud
4. Configure nginx to serve nextcloud: edit this code with your actual nextcloud instance hostname.
Paste edited contents into /etc/nginx/sites-available/nextcloud
# Version 2025-07-23
upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/run/php/php8.2-fpm.sock;
}
# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
    "" "";
    default ", immutable";
}
server {
    listen 80;
    listen [::]:80;
    server_name cloud.example.com;
    # Prevent nginx HTTP Server Detection
    server_tokens off;
    # Enforce HTTPS
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    # With NGinx >= 1.25.1 you should use this instead:
    # listen 443      ssl;
    # listen [::]:443 ssl;
    # http2 on;
    server_name cloud.example.com;
    # Path to the root of your installation
    root /var/www/nextcloud;
    # Use Mozilla's guidelines for SSL/TLS settings
    # https://mozilla.github.io/server-side-tls/ssl-config-generator/
    ssl_certificate     /etc/ssl/nginx/cloud.example.com.crt;
    ssl_certificate_key /etc/ssl/nginx/cloud.example.com.key;
    # Prevent nginx HTTP Server Detection
    server_tokens off;
    # HSTS settings
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    # set max upload size and increase upload timeout:
    client_max_body_size 512M;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;
    # Proxy and client response timeouts
    # Uncomment an increase these if facing timeout errors during large file uploads
    #proxy_connect_timeout 60s;
    #proxy_send_timeout 60s;
    #proxy_read_timeout 60s;
    #send_timeout 60s;
    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
    # Pagespeed is not supported by Nextcloud, so if your server is built
    # with the `ngx_pagespeed` module, uncomment this line to disable it.
    #pagespeed off;
    # The settings allows you to optimize the HTTP2 bandwidth.
    # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/
    # for tuning hints
    client_body_buffer_size 512k;
    # HTTP response headers borrowed from Nextcloud `.htaccess`
    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;
    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;
    # Set .mjs and .wasm MIME types
    # Either include it in the default mime.types list
    # and include that list explicitly or add the file extension
    # only for Nextcloud like below:
    include mime.types;
    types {
        text/javascript mjs;
    application/wasm wasm;
    }
    # Specify how to handle directories -- specifying `/index.php$request_uri`
    # here as the fallback means that Nginx always exhibits the desired behaviour
    # when a client requests a path that corresponds to a directory that exists
    # on the server. In particular, if that directory contains an index.php file,
    # that file is correctly served; if it doesn't, then the request is passed to
    # the front-end controller. This consistent behaviour means that we don't need
    # to specify custom rules for certain paths (e.g. images and other assets,
    # `/updater`, `/ocs-provider`), and thus
    # `try_files $uri $uri/ /index.php$request_uri`
    # always provides the desired behaviour.
    index index.php index.html /index.php$request_uri;
    # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
    location = / {
        if ( $http_user_agent ~ ^DavClnt ) {
            return 302 /remote.php/webdav/$is_args$args;
        }
    }
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }
    # Make a regex exception for `/.well-known` so that clients can still
    # access it despite the existence of the regex rule
    # `location ~ /(\.|autotest|...)` which would otherwise handle requests
    # for `/.well-known`.
    location ^~ /.well-known {
        # The rules in this block are an adaptation of the rules
        # in `.htaccess` that concern `/.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; }
        location /.well-known/pki-validation    { try_files $uri $uri/ =404; }
        # Let Nextcloud's API for `/.well-known` URIs handle all other
        # requests by passing them to the front-end controller.
        return 301 /index.php$request_uri;
    }
    # Rules borrowed from `.htaccess` to hide certain paths from clients
    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }
    # Ensure this block, which passes PHP files to the PHP process, is above the blocks
    # which handle static assets (as seen below). If this block is not declared first,
    # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
    # to the URI, resulting in a HTTP 500 error response.
    location ~ \.php(?:$|/) {
        # Required for legacy support
        rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /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 HTTPS on;
        fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
        fastcgi_param front_controller_active true;     # Enable pretty urls
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering on;                   # Required as PHP-FPM does not support chunked transfer encoding and requires a valid ContentLength header.
        # PHP-FPM 504 response timeouts
        # Uncomment and increase these if facing timeout errors during large file uploads
        #fastcgi_read_timeout 60s;
        #fastcgi_send_timeout 60s;
        #fastcgi_connect_timeout 60s;
        fastcgi_max_temp_file_size 0;
    }
    # Serve static files
    location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ {
        try_files $uri /index.php$request_uri;
        # HTTP response headers borrowed from Nextcloud `.htaccess`
        add_header Cache-Control                     "public, max-age=15778463$asset_immutable";
        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;
        access_log off;     # Optional: Don't log access to assets
    }
    location ~ \.(otf|woff2?)$ {
        try_files $uri /index.php$request_uri;
        expires 7d;         # Cache-Control policy borrowed from `.htaccess`
        access_log off;     # Optional: Don't log access to assets
    }
    # Rule borrowed from `.htaccess`
    location /remote {
        return 301 /remote.php$request_uri;
    }
    location / {
        try_files $uri $uri/ /index.php$request_uri;
    }
}
Add a symlink to sites-enabled to activate the website. The space and dot at the end of the second line is important; don’t miss that.
cd /etc/nginx/sites-enabled ln -s ../sites-available/nextcloud .
Test nginx config with nginx -tand correct errors if necessary. If no errors, reload nginx: systemctl reload nginx
**NOTE:** You will likely see errors related to SSL certs here. For Linux sysadmins, you should be able to figure out how to copy a valid cert to the server and point website config to the correct ssl cert and key. I typically use Certbot with Nginx on a reverse proxy and rsync the certs from the reverse proxy on a schedule via cron. If you need help with this, use the website contact form… maybe I should write a blog post on this.
With certs in place, you can keep going.
Presuming you’ve already set up DNS, point your browser to your chosen hostname and enter your chosen admin username and password along with the MariaDB database username and password in the setup wizard screen, and click the Install button. At the Extra Apps install screen, select the ones you want to install; wait for this process to complete. While it’s working at this, you can have the fun of updating some PHP config.
5. Update php settings for nextcloud
Edit file  /etc/php/8.2/fpm/php.ini
Update the following params (search for each key and increase values as shown; you’ll need to un-comment the opcache line by removing the semicolon at the beginning)
memory_limit = 512M upload_max_filesize = 2G post_max_size = 2G ; The amount of memory for interned strings in Mbytes. opcache.interned_strings_buffer=16
Now edit the file /etc/php/8.2/fpm/pool.d/www.confand un-comment the following lines: (remove the semi-colon from the start of each line)
env[HOSTNAME] = $HOSTNAME env[PATH] = /usr/local/bin:/usr/bin:/bin env[TMP] = /tmp env[TMPDIR] = /tmp env[TEMP] = /tmp
Restart php-fpm: systemctl restart php8.2-fpm
6. Get rid of the manifold pesky errors found by the self-check
Now in the Nextcloud interface, click your user avatar in the upper right and click Administration Settings. Mine had a Very Long List of woes and troubles to work through. To clear the error about missing database indices:
cd /var/www/nextcloud php occ db:add-missing-indices
I also had a warning about mimetype integrations, which was cleared by running the command shown:
php occ maintenance:repair --include-expensive
Let’s enable memcahce for nextcloud. Add these lines to the config array in /var/www/nextcloud/config/config.php
'memcache.local' => '\OC\Memcache\APCu', 'memcache.distributed' => '\OC\Memcache\Redis', 'redis' => [ 'host' => '127.0.0.1', 'port' => 6379, ], 'memcache.locking' => '\OC\Memcache\Redis',
If you installed all the packages shown in the first couple steps, you should be able to refresh your nextcloud admin page without a crash. If NC crashes, verify all the packages (redis, php-apcu especially) are installed.
A long list of my errors were related to the server not being able to connect to itself via its assigned hostname. This is resolved with a simple edit to /etc/hosts, resolving the DNS name of your nextcloud instance to the local server’s IP address.
With all this done, your admin page should look a lot more cheerful. I still had a few things to work through. I added this line to config.php
'default_phone_region' => 'CA',
If you’re not in Canada, replace CA with your own country code.
At this point I only had one Informational item left and that was informing me I hadn’t sent a test email. Resolve this by going to the Personal Info section (top of left side menu) and enter your email address. Then visit Basic Settings (a few options down in the Administration section of the left-hand menu) and enter SMTP server information, and send a test mail to yourself.
NOTE: If you don’t set an email address for yourself in your profile info, the test mail will fail with error 400. There’s not a meaningful error message telling you that you need to update your profile info.
If all is working well over HTTPS, edit the file /etc/nginx/sites-enabled/nextcloud; find this line, and un-comment it (remove the # at the beginning of the line)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Reload or restart nginx.
To clear the AppAPI warning, go to Apps and disable AppAPI. A brief search on that revealed that it’s mainly for installing apps that integrate with this-or-that AI. I’m very much against slamming AI into “all the things” and disabled the app without hesitation.
Search for help with other warnings or errors; the NC docs and community have a lot of help. I blew away the warning about errors in the log by erasing the contents of the Nextcloud log file; most or all of the errors there were from failed attempts at this or that config along the setup journey anyway. The log file is /var/www/nextcloud/data/nextcloud.log — or if you have chosen another location for your data store, it’s simply the nextcloud.log file in your chosen Nextcloud data directory.
The feeling of success!


 
																			 
																			 
																			