Generated: May 2026 | Platform: AWS EC2 — us-east-1 (N. Virginia)
This repository contains the complete terminal command reference and explanations for deploying a private, highly secure Nextcloud Hub instance on AWS using Ubuntu Server, Nginx, MariaDB, and Tailscale for VPN-only access.
| Component | Version | Purpose |
|---|---|---|
| Ubuntu (OS) | 24.04.4 LTS (Noble) | Server operating system |
| Nginx | 1.24.0 | Web server & reverse proxy |
| PHP-FPM | 8.3.6 | PHP application runtime |
| MariaDB | 10.11.14 LTS | Relational database server |
| Nextcloud | 33.0.3 (Hub 26 Winter) | Private cloud application |
| Tailscale | 1.96.4 | VPN mesh network |
| Redis | 7.0.15 | In-memory cache & file locking |
⚠️ Security Notice: Sensitive information including IP addresses, hostnames, usernames, passwords, key pair names, and account identifiers has been intentionally omitted from this document.
A major cause of "Code Integrity" errors, "Missing Certificate Keys," and HTTP routing failures is initializing Nextcloud before the underlying infrastructure is strictly configured.
Do NOT extract the Nextcloud archive or hit the web installer URL until the following "Pre-Flight" conditions are met:
- Database is Locked: MariaDB is secured (
mysql_secure_installation), and the dedicatednextclouddatabase and user are actively mapped. - PHP is Tuned: The
/etc/php/8.3/fpm/php.inifile has been updated to support512Mmemory limits and OPcache parameters. If Nextcloud initializes on default PHP settings, background tasks will immediately time out. - SSL is Active: The Tailscale MagicDNS certificates are generated and correctly placed in
/etc/ssl/certs/. Initializing Nextcloud over plain HTTP will permanently bake unsecured paths into the database, causing redirect loops later. - Nginx Headers are Strict: The Nginx virtual host is actively enforcing Strict-Transport-Security (HSTS) and OCM/CalDAV rewrite rules before Nextcloud runs its first internal systems check.
The Golden Rule: Extract the Nextcloud .zip and change the folder permissions (chown -R www-data:www-data) as the absolute final step before opening your web browser to finish the setup.
| Field | Value |
|---|---|
| Instance Type | t3.medium (2 vCPU, 4 GB RAM) |
| Storage | 20 GB EBS gp3 |
| Region | us-east-1 (N. Virginia) |
| Access Method | Tailscale MagicDNS over WireGuard VPN |
Before connecting to EC2, the private key file must have strict permissions. chmod 400 sets the file to read-only for the owner only. SSH refuses to use keys that are accessible by others as a security measure.
chmod 400 ~/.ssh/[your-key].pemℹ️ The key must be copied into WSL’s own filesystem (~/.ssh/) first. chmod does not work on Windows-mounted paths (/mnt/c/...).
Establishes an encrypted SSH connection to the Ubuntu EC2 instance. The -i flag specifies the private key file. All subsequent commands run inside this remote session.
ssh -i ~/.ssh/[your-key].pem ubuntu@[your-ec2-ip]ℹ️ Ubuntu 24.04 AMI uses ubuntu as the login user, not admin or ec2-user.
apt update refreshes the local package index. apt upgrade -y installs all available security patches and software updates. Always the first step on a fresh server.
sudo apt update && sudo apt upgrade -yℹ️ A new kernel was installed during this step, requiring a reboot.
After a kernel upgrade, the server must reboot to load the new kernel into memory.
sudo rebootℹ️ After reboot, reconnect using the same SSH command from Step 2.
Nginx handles all incoming HTTP/HTTPS requests. It serves Nextcloud’s static files directly and forwards PHP requests to PHP-FPM. Chosen over Apache for lower memory usage.
sudo apt install nginx -yStarts Nginx immediately and registers it to auto-start on every reboot.
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginxPHP-FPM executes Nextcloud’s PHP application code.
sudo apt install php8.3-fpm php8.3-mysql php8.3-gd php8.3-curl php8.3-xml \
php8.3-zip php8.3-mbstring php8.3-intl php8.3-bcmath php8.3-gmp \
php8.3-imagick php8.3-redis php8.3-apcu -ysudo systemctl start php8.3-fpm
sudo systemctl enable php8.3-fpm
sudo systemctl status php8.3-fpmFive key values must be increased from defaults for Nextcloud to function properly.
sudo nano /etc/php/8.3/fpm/php.iniModifications:
memory_limit = 512Mupload_max_filesize = 512Mpost_max_size = 512Mmax_execution_time = 300output_buffering = Off
Restart the service to apply changes:
sudo systemctl restart php8.3-fpmMariaDB stores all Nextcloud metadata. It is a fully open-source MySQL-compatible database.
sudo apt install mariadb-server mariadb-client -ysudo systemctl start mariadb
sudo systemctl enable mariadb
sudo systemctl status mariadbRuns an interactive security hardening script.
sudo mysql_secure_installation(Accept defaults to remove anonymous users, disable remote root login, and remove the test database).
Creates a dedicated database with utf8mb4 character set (required for full Unicode/emoji support).
sudo mysql -u root -pCREATE DATABASE [dbname] CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER '[dbuser]'@'localhost' IDENTIFIED BY '[password]';
GRANT ALL PRIVILEGES ON [dbname].* TO '[dbuser]'@'localhost';
FLUSH PRIVILEGES;
EXIT;Installs Tailscale — a WireGuard-based mesh VPN. Makes the server completely invisible to the public internet.
curl -fsSL https://tailscale.com/install.sh | shsudo tailscale upℹ️ MagicDNS and HTTPS Certificates must be enabled in the Tailscale admin console under DNS settings before proceeding.
Provisions a browser-trusted TLS certificate for the server’s MagicDNS hostname.
sudo tailscale cert [your-magicdns-hostname]Moves the certificate and private key to standard system SSL directories and enforces secure access rights.
sudo mv [hostname].crt /etc/ssl/certs/
sudo mv [hostname].key /etc/ssl/private/
sudo chmod 644 /etc/ssl/certs/[hostname].crt
sudo chmod 600 /etc/ssl/private/[hostname].keyCreates the production Nginx config including HTTP-to-HTTPS redirect, SSL certificate paths, PHP-FPM upstream, and security headers.
sudo nano /etc/nginx/sites-available/nextcloudupstream php-handler {
server unix:/run/php/php8.3-fpm.sock;
}
map $arg_v $asset_immutable {
"" "";
default ", immutable";
}
server {
listen 80;
listen [::]:80;
server_name [YOUR_TAILSCALE_IP].ts.net;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name [YOUR_TAILSCALE_IP].ts.net;
server_tokens off;
ssl_certificate /etc/ssl/certs/[YOUR_TAILSCALE_IP].ts.net.crt;
ssl_certificate_key /etc/ssl/private/[YOUR_TAILSCALE_IP].ts.net.key;
root /var/www/nextcloud;
index index.php /index.php$request_uri;
client_max_body_size 512M;
client_body_timeout 300s;
client_body_buffer_size 512k;
fastcgi_buffers 64 4K;
fastcgi_hide_header X-Powered-By;
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/xhtml+xml application/xml font/opentype image/svg+xml text/css text/plain;
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;
add_header X-XSS-Protection "1; mode=block" always;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
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\/.+|ocs-provider\/.+) /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;
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_max_temp_file_size 0;
}
location ~ \.(?:css|js|mjs|svg|gif|png|jpg|ico|wasm|tflite|map|ogg|flac)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463$asset_immutable";
access_log off;
}
location ~ \.woff2?$ {
try_files $uri /index.php$request_uri;
expires 7d;
access_log off;
}
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
access_log /var/log/nginx/nextcloud_access.log;
error_log /var/log/nginx/nextcloud_error.log;
}Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginxsudo apt install unzip -yDownloads the latest stable Nextcloud release.
cd /tmp
wget https://download.nextcloud.com/server/releases/latest.zipExtracts the zip, moves Nextcloud to /var/www/nextcloud, and sets the proper web-server ownership and directory permissions. (This triggers the actual application deployment now that the infrastructure is ready).
unzip latest.zip
sudo mv nextcloud /var/www/nextcloud
sudo chown -R www-data:www-data /var/www/nextcloud
sudo chmod -R 755 /var/www/nextcloudNote: You can now safely navigate to your Tailscale MagicDNS URL in a browser to run the Web Installer.
Nextcloud requires background tasks to run regularly.
sudo crontab -u www-data -eAdd this line:
*/5 * * * * php -f /var/www/nextcloud/cron.phpAllows SSH and Tailscale traffic, blocking all standard public web traffic for a highly secure perimeter.
sudo ufw allow ssh
sudo ufw allow in on tailscale0
sudo ufw enable
sudo ufw statusRedis provides memory caching (memcache.local) and transactional file locking (memcache.locking).
sudo apt install redis-server -y
sudo systemctl enable redis-server
sudo systemctl start redis-serversudo nano /var/www/nextcloud/config/config.phpAdd the caching parameters:
'memcache.local' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => [
'host' => 'localhost',
'port' => 6379,
],
'maintenance_window_start' => 1,
'default_phone_region' => 'US',Fix 1: PHP environment variables
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
# Change to: clear_env = noFix 2: OPcache interned strings buffer
sudo nano /etc/php/8.3/fpm/php.ini
# Change to: opcache.interned_strings_buffer = 16Apply fixes and Run Nextcloud Maintenance Repair:
sudo systemctl restart php8.3-fpm
sudo -u www-data php /var/www/nextcloud/occ maintenance:repair --include-expensiveFinal health check to confirm all services are active and enabled.
sudo systemctl status nginx
sudo systemctl status php8.3-fpm
sudo systemctl status mariadb
sudo systemctl status tailscaled
sudo systemctl status redis-server
sudo ufw status



