Self-Hosted Personal Cloud: From Old Laptop to Fully Private Cloud
Assalamualaikum and greetings from Semenyih! This is your complete A-to-Z guide for turning an old laptop and a WD My Cloud NAS into a secure, self-hosted cloud server — fully under your control and accessible from anywhere in the world.
This journey combines the best of both worlds: the raw technical depth from my original build notes and a beginner-friendly, step-by-step flow. Whether you're here to learn the basics or to dive straight into Docker configs, you'll find what you need.
Why I Built This
Western Digital discontinued remote access support for older WD My Cloud devices, essentially turning them into local-only storage. Rather than letting mine gather dust, I built a replacement cloud service using Debian Linux, Docker, Nextcloud, and Nginx Proxy Manager — bringing back remote access and adding more privacy, flexibility, and control than the original ever had. Recently, I've also rebuilt my dead WD My Cloud hard drive by replacing the failed drive with a new one — here's how I did it.
What You'll Need
- An old laptop (dedicated to this project) – This will act as your always-on home server, hosting Nextcloud and acting as the "middleman" between your WD My Cloud NAS and the internet. Tip: Even older laptops with at least 4GB RAM and a decent CPU can work well for this.
- USB drive (≥ 4GB) to install the server OS – You'll use this to create a bootable installer for your Linux server operating system (e.g., Ubuntu Server). Note: Larger USB drives are fine, but 4GB is the minimum for most modern Linux server ISOs.
- WD My Cloud NAS (or similar) on your local network – This is your existing network-attached storage device where your files live. We'll mount it to the server so Nextcloud can manage and serve those files securely over the internet.
-
A domain name + DNS access (like cPanel) – A domain makes it easier to access your server
(e.g.,
cloud.example.com) instead of remembering an IP address. You'll need DNS control so you can point your domain/subdomain to your server's IP. - Basic Linux command-line knowledge – You should be comfortable running commands in the terminal (e.g., SSH, package installation, editing config files). You don't need to be an expert, but familiarity will save time and prevent mistakes.
Step 1: Install the Server OS (Debian 12)
- Download the Debian 12 "Bookworm" netinst ISO from debian.org/distrib.
-
Create a bootable USB installer:
- Use BalenaEtcher (cross-platform)
- Or use Rufus (Windows only)
-
Boot your laptop from the USB and start the Debian installation.
- On the "Software selection" screen, uncheck "Debian desktop environment" and check
only:
-
SSH server -
standard system utilities
-
- When prompted for a
rootpassword, leave it blank — the main user you create will havesudoprivileges.
- On the "Software selection" screen, uncheck "Debian desktop environment" and check
only:
💡 Why 50 GB on the second drive?
In this setup, the operating system (Debian) will be installed on my 120 GB SSD for speed and reliability. My laptop also has a 500 GB HDD for bulk storage. I will dedicate 50 GB of that HDD to store Nextcloud's application data and configuration files. The remaining ~450 GB of the HDD will stay free, which I can later mount as my main NAS storage area.
This approach keeps your OS and Nextcloud separate:
- If you ever reinstall Debian, your Nextcloud data is not wiped (as it lives on a different drive).
- The OS runs from the faster SSD, while the larger HDD holds bulk files.
📏 Creating the 50 GB partition on /dev/sdb with parted
- After Debian is installed (or during install if you prefer manual partitioning), open a terminal and run:
sudo parted /dev/sdb - Inside parted, set a GPT partition table (warning: erases all data on /dev/sdb):
(parted) mklabel gpt - Create a new 50 GB primary partition at the start of the HDD:
(parted) mkpart primary ext4 1MiB 50GiB - Optionally create another partition for the rest of the HDD:
(parted) mkpart primary ext4 50GiB 100% - Check your work:
(parted) print - Exit parted:
(parted) quit - Format the 50 GB partition for Nextcloud:
sudo mkfs.ext4 /dev/sdb1
Later in this guide, we'll mount /dev/sdb1 to /media/nextcloud so Docker can store
Nextcloud's files there.
Step 2: Install Docker & Docker Compose
-
Log in to your server via SSH
If you're not already on your Debian server, connect from another machine using:
Replacessh your_username@SERVER_IPyour_usernamewith the user you created during Debian installation, andSERVER_IPwith your server's IP address.
Tip: You can find the server's IP by runningip adirectly on the machine. -
Update all existing packages
Run:
-sudo apt update && sudo apt upgrade -ysudo apt updatefetches the latest package lists from Debian repositories.
-sudo apt upgrade -yupgrades all installed software to their latest versions without asking for confirmation.
Keeping your system updated before installing Docker helps prevent compatibility issues. -
Install Docker
The official convenience script from Docker makes installation easier:
-curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.shcurl -fsSLdownloads the script securely.
--o get-docker.shsaves it as a file namedget-docker.sh.
-sudo sh get-docker.shruns it with administrator privileges.
This will install both the Docker Engine and the Docker CLI on your Debian system. -
Add your user to the Docker group
By default, Docker commands requiresudo. To run them without typingsudoevery time, add your user to the Docker group:
-sudo usermod -aG docker your_username-aGappends your user to the specified group without removing other group memberships.
Important: You must log out and log back in (or reboot) for this change to take effect. -
Verify Docker installation
After logging back in, test Docker by running:
If it prints a welcome message, Docker is installed and working correctly.docker run hello-world -
Install Docker Compose (v2)
Docker Compose lets you run multi-container setups like Nextcloud + database. On modern Docker installations, Docker Compose v2 is included as a plugin, so you can check with:
If it shows a version number, you're ready to go. If not, you can install it via:docker compose versionsudo apt install docker-compose-plugin
Step 3: Point Your Domain Name
Before you can securely access your Nextcloud from anywhere, you'll need to connect your server to a domain
name.
This step ensures that instead of typing your server's numeric IP address, you can simply type something easy
like
cloud.yourdomain.com in your browser.
-
Find your public IP address:
Your public IP is the internet-facing address of your home or server. Run:curl -4 ifconfig.meThis will return a number like
123.45.67.89. Write this down, because you'll need it in the next step. -
Point your domain to your server:
Log in to your hosting control panel (for example, cPanel). Go to Zone Editor (or DNS Manager) and create a new A record:- Name:
— this means your Nextcloud will be atcloudcloud.yourdomain.com - Address: your public IP address from step 1
Once saved, DNS changes usually take between 5 minutes and 1 hour to propagate globally, but can sometimes take up to 24 hours.
- Name:
-
If your ISP uses a dynamic IP address:
Some ISPs change your public IP address periodically. If that happens, your domain will stop pointing to your server unless you update it each time. To avoid this hassle, you can use a Dynamic DNS (DDNS) service that automatically updates your domain when your IP changes. -
Optional (Free DDNS option):
If you don't have a domain name, you can register for a free DDNS service and point it directly to your home IP. I recommend No-IP — using my referral link gives me $5 No-IP Credit which help me in some ways, and your will get 20% OFF any new service with coupon codeREFER20.With No-IP, you could get a hostname like
myawesomecloud.ddns.netfor free, which will automatically stay linked to your changing IP address.
Why this step matters: Without linking a domain (or DDNS) to your IP, you would have to remember a long number that may even change over time. Using a domain name makes your cloud setup user-friendly, professional-looking, and easier to secure with HTTPS.
Step 4: Create the Docker Environment
In this step, we will set up the Nextcloud server, a database, and a reverse proxy using Docker Compose. This setup will run three separate containers:
- Nextcloud – The main application for your personal cloud storage.
- MariaDB – A reliable database engine used by Nextcloud to store data.
- Nginx Proxy Manager – A simple interface to manage HTTPS certificates and domain routing.
- Create the project directory (this keeps all your config files together):
mkdir ~/nextcloud-stack && cd ~/nextcloud-stack - Create the
file (this file tells Docker how to run the containers):docker-compose.ymlversion: '3.8' //This is deprecated, remove thisservices: nextcloud: image: nextcloud:latest restart: unless-stopped volumes: - ./nextcloud_config:/var/www/html/config - /mnt/nextcloud_data:/var/www/html/data - /mnt/nas_share:/media/nas environment: - MYSQL_HOST=db - MYSQL_USER=nextcloud - MYSQL_PASSWORD=your_strong_db_password - MYSQL_DATABASE=nextcloud depends_on: - db db: image: mariadb:10.6 restart: unless-stopped command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW volumes: - ./mariadb_data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=your_strong_root_password - MYSQL_PASSWORD=your_strong_db_password - MYSQL_DATABASE=nextcloud - MYSQL_USER=nextcloud proxy: image: 'jc21/nginx-proxy-manager:latest' restart: unless-stopped ports: - '80:80' # HTTP - '443:443' # HTTPS - '81:81' # Nginx Proxy Manager Admin UI volumes: - ./npm_data:/data - ./npm_letsencrypt:/etc/letsencryptNote: Replace
your_strong_root_passwordandyour_strong_db_passwordwith long, random passwords for better security. - Create the storage directories for Nextcloud data and NAS mount point:
sudo mkdir /mnt/nextcloud_data /mnt/nas_share/mnt/nextcloud_data will store uploaded files from Nextcloud.
/mnt/nas_share will be the mount point for your NAS drive. - (Optional but recommended) Set correct permissions so Docker containers can access the storage:
sudo chown -R 33:33 /mnt/nextcloud_dataUID/GID
33:33is the defaultwww-datauser in the Nextcloud container. - Start the environment:
docker compose up -dThis will download the necessary images and run them in the background.
- Verify all containers are running:
docker psYou should see three containers:
nextcloud,db, andproxy.
Security Tip: Store your passwords in a secure password manager. Avoid hardcoding them in public repositories.
Step 5: Mount Your WD My Cloud NAS and 50GB Local Partition
- Install CIFS tools (for NAS access):
sudo apt install cifs-utils - Create credentials file for NAS:
sudo nano /etc/samba/credentials
Add:
Secure it:username=your_nas_username password=your_nas_password
sudo chmod 600 /etc/samba/credentials - Find UUIDs for your drives:
sudo blkid
Look for the 50GB partition (likely/dev/sdX) and note itsUUID. - Edit
to include both NAS and local 50GB partition:/etc/fstab
Replace# Mount NAS for Nextcloud External Storage //192.168.1.120/YOUR_FILE /mnt/nas_share cifs credentials=/etc/samba/credentials,uid=33,gid=33,iocharset=utf8,vers=2.0,dir_mode=0770,file_mode=0660,_netdev 0 0 # Primary Nextcloud Data on local 50GB drive UUID=YOUR_50GB_UUID_HERE /mnt/nextcloud_data ext4 defaults,errors=remount-ro 0 2YOUR_50GB_UUID_HEREwith the UUID from theblkidoutput. - Create mount points (if they don't already exist):
sudo mkdir -p /mnt/nas_share /mnt/nextcloud_data - Mount everything:
sudo mount -a
Step 6: Initial Nextcloud Setup
- Start all Docker containers:
In your project directory (where yourdocker-compose.ymlfile is located), run:
This will pull the necessary images (if not already downloaded) and start the containers for Nextcloud, the database, and Nginx Proxy Manager in the background.docker compose up -d - Configure Nginx Proxy Manager (NPM):
- Open your web browser and go to:
Replacehttp://SERVER_IP:81SERVER_IPwith your server's LAN IP address (e.g.,192.168.1.150). - Log in using the default credentials (if first time):
You will be prompted to set a new email and password.Email: admin@example.com Password: changeme - Go to Hosts → Proxy Hosts → Add Proxy Host.
- Fill in:
- Domain Names:
cloud.yourdomain.com - Scheme:
http - Forward Hostname / IP:
nextcloud - Forward Port:
80
- Domain Names:
- Under the SSL tab:
- Select Request a new SSL certificate.
- Tick Force SSL, HTTP/2 Support, and HSTS Enabled.
- Choose Let's Encrypt and enter a valid email for certificate registration.
- Click Save. Nginx Proxy Manager will now route all HTTPS traffic for
to your Nextcloud container.cloud.yourdomain.com
- Open your web browser and go to:
- Complete Nextcloud's installation:
- In your browser, visit:
https://cloud.yourdomain.com - Create an admin account by entering:
- Username: Choose your Nextcloud admin username, e.g.:
admin - Password: Choose a strong password.
- Username: Choose your Nextcloud admin username, e.g.:
- Set the Data folder to your mounted storage directory
/mnt/nextcloud_data - Click Finish Setup. Nextcloud will initialize its database, create necessary directories, and start your personal cloud environment.
- In your browser, visit:
Step 7: Add NAS as External Storage
-
Enable "External Storage Support" in Nextcloud Apps
By default, Nextcloud has an optional app called External Storage Support that allows you to mount local directories, other network shares (SMB/CIFS, FTP, S3, etc.), and present them to users inside Nextcloud. This is what we'll use to connect our NAS.
- Log in to your Nextcloud instance as an administrator.
- Click your profile icon in the top-right corner → Apps.
- In the left sidebar, select Disabled apps or use the search bar to look for
External storage support - Click Enable. (This app is officially maintained by Nextcloud, so it's safe to enable.)
-
Configure NAS as Local External Storage
Now that the app is active, we'll tell Nextcloud where the NAS data is mounted inside the Docker container, in our case:
/media/nas- Go to Settings (via the profile menu).
- In the left-hand sidebar under Administration, click External storage.
- At the bottom of the list, add a new mount point:
- Storage type: Local
- Folder name:
(This is the name users will see in their file list.)NAS - Configuration:
(This is the bind mount path from the Docker/media/nasvolumes:section in Step 4.) - Available for: leave blank to make it global, or select specific users/groups.
- If you see a green circle icon next to the entry, it means the mount is detected and accessible by Nextcloud.
- If you see a red square, check:
- The mount path exists inside the container.
- Correct permissions:
(sudo chown -R 33:33 /mnt/nas_share33:33is the www-data UID:GID used by Nextcloud.)
-
Scan Files to Populate the File Index
Nextcloud maintains an internal database of files. Since we're adding an external directory, we need to scan it so files appear in the web UI immediately.
docker compose exec --user www-data nextcloud php occ files:scan --all-
ensures the command runs with Nextcloud's web server permissions.--user www-data -
scans all users' storages. You can target a single user with:--allphp occ files:scan username - The scan time depends on the number of files on the NAS.
Once the scan completes, the NAS files should now be visible in the NAS folder in Nextcloud.
-
Troubleshooting Tips
-
Permission errors:
If Nextcloud cannot read or write files, check ownership of your data directory. The
user (UID 33, GID 33 in Debian/Ubuntu-based systems) should own all Nextcloud data. Run:www-data
This ensures Nextcloud can manage uploads, previews, and file syncing without hitting permission-denied errors. For external mounts (like NAS shares), ensure the mount point itself is accessible tosudo chown -R 33:33 /mnt/nextcloud_data
and that any underlying filesystem supports required permissions.www-data -
Mount errors (Error 95):
If you encounter "mount error(95): Operation not supported" when mounting SMB/CIFS shares, the SMB protocol
version may be incompatible. Update your
/etc/fstabentry to try differentversvalues:vers=3.0If that fails, tryvers=2.0. Some older NAS devices (like WD My Cloud) may only work withvers=2.0, but note this is less secure. After editing, remount with:sudo mount -a -
DNS not working:
If your custom domain (e.g.,
cloud.example.com) does not resolve, it may be due to slow or incomplete DNS propagation. Use tools like dnschecker.org to verify your domain's A/AAAA records have propagated globally. If they have not, wait up to 48 hours, or check your registrar's DNS settings for typos or missing records. - Static IP: Assigning a static local IP to your Nextcloud server prevents connectivity issues after router restarts. Use your router's admin interface to set a DHCP reservation for your server's MAC address. This ensures port forwarding, SSL, and reverse proxy rules always point to the correct device.
-
Reverse proxy errors:
If you're using Nginx Proxy Manager (NPM) or similar, verify:
- Your domain is pointing to your public IP.
- Port forwarding for HTTP (80) and HTTPS (443) is enabled on your router to your proxy host.
- SSL certificates are valid and not expired.
- For websocket or long-polling support, enable proxy buffering adjustments in NPM.
-
Content Security Policy (CSP)
form-actionlogin issue: If you see an error like:Refused to send form data to 'https://cloud.example.com/index.php/login' because it violates the following Content Security Policy directive: "form-action 'self'".This usually means your proxy or Nextcloud's CSP headers are blocking cross-domain form submissions.- Ensure you are accessing Nextcloud from the exact same URL configured in
config/config.phpunder'trusted_domains'and'overwrite.cli.url'. - If using Nginx Proxy Manager, remove any conflicting CSP settings in the "Advanced" tab for that host.
- Inside Nextcloud's container or installation, check
.htaccessandconfig/config.phpfor hardcoded CSP rules. - If you have custom headers in your reverse proxy, ensure
form-actionincludes your full domain (e.g.,form-action 'self' https://cloud.example.com).
- Ensure you are accessing Nextcloud from the exact same URL configured in
Fixing Nextcloud Admin Dashboard Warnings
Here's how you can fix each of those Security & Setup warnings from your Nextcloud admin dashboard. These can all be done inside your Docker Nextcloud setup.
-
Trusted proxies setting not correctly set:
# Enter container docker exec -it nextcloud-stack-nextcloud-1 bash # Edit config.php vi /var/www/html/config/config.php # Add: 'trusted_proxies' => array (0 => 'nextcloud-stack-proxy-1',// Remove this 0 => '172.18.0.3', // Replace with your proxy container's internal Docker IP ), # Get proxy container IP docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nextcloud-stack-proxy-1 # Restart Nextcloud docker restart nextcloud-stack-nextcloud-1 -
No maintenance window configured:
docker exec -u www-data -it nextcloud-stack-nextcloud-1 php occ config:system:set maintenance_window_start --type=integer --value=1 # Value is hour in UTC (0–23) -
Mimetype migrations available:
docker exec -u www-data -it nextcloud-stack-nextcloud-1 php occ maintenance:repair --include-expensive -
HSTS header not set:
In Nginx Proxy Manager → Edit Proxy Host → Advanced tab, add:
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
In Nginx Proxy Manager → Edit Proxy Host → SSL tab:
Toggle HSTS Enabled and HSTS Subdomains on
Then restart Nginx Proxy Manager.
-
Default phone region not set:
'default_phone_region' => 'MY', // Example: MY for Malaysia, US for USAFull ISO codes: List of country codes
-
Email server not configured:
In Nextcloud: Settings → Basic settings → Email server → set SMTP or sendmail → Send email to verify.
✅ After all changes:
docker restart nextcloud-stack-nextcloud-1
Then refresh the admin page to confirm warnings are gone.
Congratulations — you now have a fully functional, secure, self-hosted cloud. With this setup, your old laptop gets a new life, your WD My Cloud regains remote access, and your data sovereignty is fully restored.
Support My Work
If this blog post helped you, please consider a small donation. Your support helps keep this content free and accessible for everyone. Thank you!
Donate with PayPal
The easiest way to support me is by buying me a coffee through PayPal. It's quick, secure, and uses a trusted platform.
Buy Me a Coffee! or a new Macbook!Donate with Crypto
You can also support me with cryptocurrency. Just copy the address of your preferred coin below.

Comments
Post a Comment