Setting up a home cloud with Raspberry Pi
I have set up Nextcloud in a Raspberry Pi running here at home. My goal is two-fold: firstly I want to have a file sharing solution that works across devices and operating systems, and secondly I also want to know where the data is (and by extension, who can access it!).
Calling this a "home cloud" is a bit of a stretch: given how humble my goals and the machine it is running on, it's more akin to a "puff" or just "light fog", but I suppose it's descriptive enough for folks to find this post if they need to do something similar.
'Home' really means home
Before I describe the steps I took, I want to highlight the home aspect, as this service is really staying home.
It is not exposed to the Internet. There's no port forwarding, firewall opening or anything like that: it is available only when I am in the home network. This might or not be convenient for you, but it conditions some of the decisions I took.
The consequence of this is that you can only sync when you're home. That could be an issue if I wanted to edit the same content in different machines when I'm not home, but in practice I normally only work on one thing at a time, from beginning to end, on one machine at a time. And no one else is accessing these files so there should be no issues with simultaneously editing files.
In other words, I want a unified entry point for my files, but I don't need it to be very sophisticated. So this setup works well for me.
I am also not hosting EVERYTHING in this device. It's only for things that I need to have handy in most devices, a bit like a "virtual workspace". For everything else, meaning files I don't need to access all the time, I have another drive, which is actually RAID storage so data is written to two physical devices.
I initially doubted around the storage aspect as I didn't know whether to connect an external drive to the Pi rather than using the SD card or what... But I will see how fast or not it is. This is a bit of an experiment, after all.
Finally, since it's home, I might turn it off from time to time. Yes. My cloud does not need 24/7 availability. It can be off when I go away, or I could even... turn it off at night! This might actually be nice for the planet!
Tech setup
I am running this on a Raspberry Pi 3 from 2016. It was not being used so I thought I could use this spare hardware. In theory it is "slow", but so far it's been OK: I don't find it slower than accessing other "cloud drive" solutions hosted elsewhere, and in fact I find it's snappy, so I have no complaints.
The distribution is Raspberry Pi OS Lite Bookworm 64-bit, i.e. without the desktop interface, as it does not need to have one.
This machine does have wifi access but I also connected it directly to my home router with an ethernet cable, and fixed its IP address in the router config, so it always uses the same value. I haven't needed it, but could be handy if things get diabolically messy and I want to debug what is wrong with the machine without connnecting a screen and keyboard to it.
This Raspberry Pi's local name is cloudberry. You can chose any name you like, as long as you remember it, obviously!
I also enabled SSH access in the machine before flashing the image to the SD card, using the Raspberry PI imager utility.
In my laptop, I did two things to quickly login by typing ssh cloudberry. First, I created a public key and copied it using ssh-copy-id. Then I also set up an alias on my laptop's ssh config file, including the remote's login name, so I don't have to specify it when logging in. This makes logging into the machine to edit anything a breeze!
One more thing: I have chosen not to have Nextcloud "own" the entire server. It's served from the /nextcloud path because I might want to host other utilities in that machine in the future, so I'd rather put Nextcloud in its own folder from the start, and avoid having to move things afterwards. Thus, the URL to access my local Nextcloud instance will be https://cloudberry/nextcloud rather than just https://cloudberry. Most tutorials I've seen seem to assume you'll use the machine only for Nextcloud. But I don't!
Setting up the machine
As this was a fresh install, I needed to install everything. Your situation might be different, in which case you can probably figure out what's missing / what's superfluous.
Also note these are my edited notes after figuring out how to set up everything. I went back and edited everything to summarise and "smoothen" things out, but there might be some typos or phrases that do not quite make sense. If you spot any of those, please let me know!
Initial packages
Nextcloud is a PHP application, so it needs a web server to handle requests, and a database server to store file metadata (apart from actually storing the file contents in the file system, that is). I'm using Apache as web server and MariaDB as database server, but there are other options if you dislike either.
sudo apt install apache2 mariadb-server libapache2-mod-php vim
sudo apt install php-gd php-json php-mysql php-curl php-mbstring php-intl php-imagick php-xml php-zip
sudo service apache2 restart
Database setup
We want to create a user and database for Nextcloud, before running its setup tool.
sudo mariadb
CREATE USER 'nextcloud' IDENTIFIED BY 'password';
CREATE DATABASE nextcloud;
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@localhost IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
quit
Replace password with the actual password you want to use, and make a note of it so you can provide it to the installer in the next step.
Download Nextcloud
You can get it if you go to the Install page and select 'archive', but I don't like downloading things this way because it's not always clear to me which exact version of the code was used to create the archive.
So I prefer to use git. This machine didn't have it installed yet so I installed it. But downloading the whole repo was taking ages because it has a lot of history, so I use a shallow clone of a specific tag.
This is how:
sudo apt install git
cd /var/www/html
sudo git clone --branch v31.0.1 --depth 1 https://github.com/nextcloud/server.git nextcloud
Sensible permissions and groups
You also need to set up sensible permissions and group ownership or you'll have trouble or git will complain (or both) in subsequent steps.
# make the group bit sticky
sudo chmod -R g+s /var/www/html/nextcloud
# this adds your username to the www-data group
sudo usermod -a -G www-data yourusername
# acquire ownership of the files
# git got picky if I didn't own the directories when updating submodules
sudo chown -R yourusername:www-data /var/www/html/nextcloud
# let the group write to the folder
sudo chmod -R g+w /var/www/html/nextcloud
Set Up Apache subdirectory settings
As mentioned, I don't want to run Nextcloud on its own, taking over the whole server. I want it to run on a subdirectory, like cloudberry/nextcloud.
For that, I add a new config file to Apache:
sudo vim /etc/apache2/conf-available/nextcloud.conf
and add this - of course, replace cloudberry with the server name you're using:
ServerName cloudberry # your domain
<Directory /var/www/html/nextcloud/>
Require all granted
AllowOverride All
Options FollowSymLinks MultiViews
SetEnv HOME /var/www/html/nextcloud
SetEnv HTTP_HOME /var/www/html/nextcloud
</Directory>
Then:
# enable the conf file we created
sudo a2enconf nextcloud
# and restart apache
sudo systemctl restart apache2
You should have no errors! If you find errors follow the instructions and pointers in the error message, and try to figure out where you messed up (it's often a typo because of copying and pasting incomplete snippets, or having inserted them in the wrong file or place in the file).
Installing git submodules
To test this works so far, if you go to http://cloudberry/nextcloud/ you might get an error message because we got the code via git. If you got the code via the ZIP file, you might not get this error. Something like this:
"Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty"."
To fix the error, do:
cd /var/www/html/nextcloud/3rdparty
git submodule update --init
If you get a message about "dubious ownership", make sure you recursively set up the user you're logged in as the owner of the nextcloud folder, as described above (chown with -R).
The following advice printed by git did nothing to help me:
To add an exception for this directory, call:
git config --global --add safe.directory /var/www/html/nextcloud
Only changing the ownership did!
Separately, initialising the submodules takes a bit of time. When that finishes, if you go to http://cloudberry/nextcloud/index.php again you might finally get the config page for the installer.
Enter the config settings (that's why you wanted to create the database and user ahead of time!) and allow it to finish installing; this also takes a bit of time (maybe that's what they meant when they say this machine is "slow").
It might then redirect to the wrong URL in http://nextcloud/... rather than http://cloudberry/nextcloud/... but you're clever, and know what you want, so correct the URL to go to the right URL, and confirm the installation is successful.
A screen with the recommended core apps is shown. Disable or left checked whatever you want, then install the apps you want. Nextcloud can be used for collaborating, and it shows here!
Add user(s)
You've created an admin user during installation, but it's better to use a non-admin user in the day to day of using any service you operate.
Once apps finish installing, you can add new user(s) by going to Settings - click on top right menu and select Accounts.
First create a group, and call it for example normalusers.
Then click on the top left button "+ New account". And create as many user(s) as you need, using the normalusers group. I only needed one, so I did that only once.
Checking the installation "quality"
The default installation is OK to start with, but there are probably things that could be improved for a better "quality" of installation.
Nextcloud has a helpful page that can suggest things to fix/improve. You can go to http://cloudberry/nextcloud/index.php/settings/admin/overview to get security & setup warnings as detected by Nextcloud.
There are also other useful suggestions that I took into account from other websites, fundamentally about fine tuning PHP so it works better when it comes to handling file uploads.
vim /etc/php/8.2/apache2/php.ini
And find and adjust the following settings (it's not copy and paste, you need to edit existing values):
memory_limit = 512M
upload_max_filesize = 1G
post_max_size = 1G
max_execution_time = 360
date.timezone = [your_timezone]
Save and exit the file. Restart Apache to apply the changes:
sudo systemctl restart apache2
If you get an error like this when restarting Apache:
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
you can fix it by adding the host name to /etc/hosts:
127.0.1.1 cloudberry.local cloudberry
Enable mods that should be ON so we can do nice things
A number of modules that we'll need for later are not enabled by default, so you should enable them now to save you errors or going backwards later:
sudo a2enmod rewrite
sudo a2enmod ssl
sudo a2enmod headers
If you get an error like: Error re well known Your web server is not properly set up to resolve .well-known URLs, failed on: /.well-known/webfinger For more details see the... (ref).
This might be happening because Nextcloud is not in the root folder, but in a subdirectory.
Create .htaccess file in /var/www/html with these contents:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^/\.well-known/carddav /nextcloud/remote.php/dav [R=301,L]
RewriteRule ^/\.well-known/caldav /nextcloud/remote.php/dav [R=301,L]
RewriteRule ^/\.well-known/webfinger /nextcloud/index.php/.well-known/webfinger [R=301,L]
RewriteRule ^/\.well-known/nodeinfo /nextcloud/index.php/.well-known/nodeinfo [R=301,L]
</IfModule>
Also add to nextcloud.conf in /etc/apache2/conf-available/:
<Directory /var/www/html>
AllowOverride All
</Directory>
And restart! Again!
Self signed certificate
Note: I advise that you attempt this part only once you have got the things above working!
The Nextcloud server is not exposed to the Internet, and it's not meant to move from its physical location. But the clients can and do move, as they're run in laptops.
Imagine that I connect somewhere using public wifi, and a client on my device tries to access http://cloudberry/nextcloud. Since it is not encrypted, a malicious agent in the public wifi access point could very easily get ahold of my request and pretend to be cloudberry.
My laptop has no way of knowing if it is or not the real server, since it's never been "certified". So my client goes and sends my credentials to this "pretend-cloudberry" server, just by POSTing to log in. The software in the access point server could be storing this and any other data it can get about me so that they can try to use these credentials elsewhere to impersonate me. Not nice! We don't want this!
The solution is to use SSL, so that the communications between the client(s) and cloudberry are encrypted. That way, no one can eavesdrop, and I should not send my credentials to evil actor, even if accidentally.
To use SSL we need a certificate.
Nowadays people use Let's Encrypt to obtain one of those certificates, but the way it works is that you have to prove you can control the domain you want to obtain a certificate for. But this machine is not exposed to the Internet, it has no domain we control---anyone in the world can create a cloudberry domain for their local machines! I don't have the exclusive ownership.
So we can't use Let's Encrypt to get a certificate, unless we perform some convoluted moves (like using DNS TXT record challenges).
I don't have programmatic control of my DNS records and I didn't want to figure that out right now. Instead, I decided to go for a good old self-signed SSL certificate. In contrast with Let's Encrypt certificates, these are not automatically renewed, but I can live with that. Since I'm writing this post, I should be able to repeat this part of the process with ease in the future (I know that I won't remember how to do this when the time comes).
To get a certificate for 1000 days (for example):
sudo apt-get install openssl
sudo openssl req -x509 -nodes -days 1000 -newkey rsa:2048 -keyout /etc/ssl/private/apache.key -out /etc/ssl/certs/apache.crt
It will ask you a number of questions, which you can answer to your liking, except the domain that needs to correspond to the domain you want to use (cloudberry in my case). In general, it's a good idea to enter values that you'll recognise when you see them again (you will see them again shortly).
Optional step:
# generating the Diffie-Hellman group
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
Using DH helps with preventing other types of attacks, but generating that takes a while (maybe 30 min? in this underpowered device), so I recommend you open another terminal and continue preparing config files in the meantime.
Create this conf file:
sudo vim /etc/apache2/conf-available/ssl-params.conf
and paste:
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder On
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
SSLCompression off
SSLUseStapling on
SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
SSLSessionTickets Off
SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"
then save and quit. Don't restart anything yet! We need to configure more things still.
First we configure the default SSL virtual host so it points to the SSL certificate we created:
sudo vim /etc/apache2/sites-available/default-ssl.conf
Find the lines with SSLCertificateFile and SSLCertificateKeyFile and edit them so they look like so:
SSLCertificateFile /etc/ssl/certs/apache.crt
SSLCertificateKeyFile /etc/ssl/private/apache.key
i.e. they point to the certificate files we created earlier.
Also make sure the ServerName, ServerAdmin, and DocumentRoot directives are pointing to the correct locations.
You might want to look at the values from /etc/apache2/sites-available/000-default.conf for reference. And if a directive is missing, add it!
These are the right values for me:
ServerName cloudberry
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
Assuming the Diffie-Hellman values calculation has finished, you can enable the two things you edited/modified now:
sudo a2ensite default-ssl
sudo a2enconf ssl-params
Then
systemctl reload apache2
Now that you've restarted the server, try accessing it with https://cloudberry - does it serve content over https://?
If you try that with a browser, you should get a big scary certificate warning. For example, in Firefox, you would get something like this:

If you see that, then that's very good! It means the Pi is serving content with https. But is it the right certificate?
Click "Advanced... View Certificate" to check it's the certificate we generated earlier. The values should sound familiar to you. If all OK, tick "Accept the risk and Continue".
Now we want to avoid serving unencrypted content altogether. For that we will do two things.
First create /var/www/html-80/ and /var/www/html-80/index.html with some test content like 'hi' (it does not need to be a full valid HTML doc).
Then edit /etc/apache2/sites-available/000-default.conf to point its DocumentRoot to the test content:
DocumentRoot /var/www/html-80/
And reload apache: sudo systemctl reload apache2.
Check that the http:// version serves you the test content: you should now see just a hi instead of the default Apache server "It works!" page you could see before. You should still be able to see content in https://, so check that's right as well.
The second step will be to redirect all requests from 80 (http, unencrypted) to 443 (https, encrypted):
Add this to /etc/apache2/sites-available/000-default.conf
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://cloudberry/$1 [R,L]
There is a number of comments in that file, but the important section of the file should look like this now:
ServerName cloudberry
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html-80
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://cloudberry/$1 [R,L]
If you restart Apache again, and try to access any page with http:// you should be automatically redirected to the same path, but with an https:// protocol.
We're using SSL now! Yay!
Clients
Apart from just using the website interface to upload or download files to your cute little cloud, there are clients that you can install for using in the file explorer of your computer, and even in your phone.
- The mobile app for iOS works fine. I installed it from the App Store. I had used the non SSL set up beforehand so when I tried to use the SSL installation it showed a warning about the certificate and it took me restarting the app, accepting the new certificate and a bit of back and forth like that until it eventually logged in again and loaded the interface successfully.
- The Linux client in Ubuntu: I installed "Nextcloud Desktop" from the Software app. It logs in using the browser for auth. No major issue. It then syncs to
~/Nextcloud, although you can configure that to sync to another directory if you want. I like the progress updates of what it is doing! You can also install it by downloading from the Nextcloud website, as described in their docs.
Bonus: a few helpful commands for managing Apache
I don't interact with Apache as much as I used to, so this is mostly a helpful reminder for myself:
Getting info
This prints a lot of information about the current service including child tasks and logging messages.
sudo systemctl status apache2.service -l --no-pager
The -l --no-pager can be omitted, but then you have to scroll up or down to see status data and then quit manually with q when you're done.
You can also test the config syntax before you reload/restart the service. This is not so important in a test/local environment but you don't want to load the wrong config file on a live environment:
sudo apachectl configtest
Enabling/disabling things
Apache has a number of "things" that can be turned on and off, such as config files, sites and modules. Traditionally you'd place configuration files for said "things" in certain directories like sites-available, and if you wanted to enable a certain site, you'd make a symbolic link from the file corresponding to the site to a sites-enabled directory.
I suppose if you're half-asleep you can mess those up, so maybe that's why a number of corresponding commands exist nowadays to do the same things quickly. They start with a2 (for Apache 2, I guess!), then en or dis (enable/disable) and finally the thing to change conf, site, mod.
Or more programmatically: a2(en|dis)(conf|mod|site) name
I'm nice so I made you a table!
| what | enable | disable |
|---|---|---|
| conf | a2enconf name | a2disconf name |
| mod | a2enmod name | a2dismod name |
| site | a2ensite name | a2dissite name |
Note that name is assumed to end with .conf and be in the corresponding directory (conf-available, mods-available or sites-available). In case of modules a mod_ is prepended to the name e.g. a2enmod ABC would try to find a mod_ABC.conf file.
Examples:
- To enable the
nextcloud.confwhich is in/etc/apache2/conf-available/:sudo a2enconf nextcloud - To enable the rewrite module:
sudo a2enmod rewrite
Starting/stopping/reloading
Reload it
sudo systemctl reload apache2.service
Restart it
I think that reload loads the config again but does not restart the service (so existing connections would not be dropped).
sudo systemctl restart apache2.service
also if your config was faulty I am not certain if you can always reload if the server could not start the last time!
Modules
Which ones are loaded?
apache2ctl -M
At a glance, is a module enabled?
apache2ctl -M | grep rewrite
If not, turn it on:
sudo a2enmod rewrite
Bonus 2: Paranoia
Once you start learning about ways that people can attack you or your data, it's hard to stop.
Here are some pointers and thoughts that I haven't fully elaborated yet:
- Guaranteeing connection to the legitimate machine:
- we have SSL, but what if someone tries to impersonate using a machine with the same name and a certificate too?
- if they self-signed it, I should get a "heads up" sign like I did with mine
- can it be not self-signed but for a local domain like mine? i.e. can it squeeze through without raising any alarm?
- Since I have a fixed IP address for the machine, I could also add an entry on the
/etc/hostsof my laptops to ensure they skip DNS resolutions and just go to the IP address. If it's not available, then it'll timeout.- Unfortunately, I don't think this is easily configurable with a phone. I think?
- we have SSL, but what if someone tries to impersonate using a machine with the same name and a certificate too?
- Encryption
- The data in the machine is not encrypted. Should it be encrypted?
- The process seems easy enough though, so I might.
- It's not clear to me if it's also encrypted when copied to another client, e.g. a phone. But my phone does use encryption. In theory. At this point: paranoia.
- The process seems easy enough though, so I might.
- The data in the machine is not encrypted. Should it be encrypted?
- Trust:
- at the end of the day this is code written by someone else and running in my machine, do we trust it?
- arguably I could audit it...
- or trust it?
- it should be better than proprietary solutions that aren't necessarily scrutinised?
- likewise, any other software that runs in the machine could have access to the data
- it can malfunction, it can be malicious, etc.
- do we trust it behaves OK?
- it can malfunction, it can be malicious, etc.
- likewise, any other software that runs in the machine could have access to the data
- it should be better than proprietary solutions that aren't necessarily scrutinised?
- extrapolation: there could also be bad actors in my network!
- e.g. "innocent" apps that actually do more than they purport to do
- we're protected against eavesdropping in the connection between the web server and clients, but we've not protected the machine itself because we assumed this was a trusted environment
- could it benefit from anti brute force protection and those things you normally install in public facing servers? like disabling password based log ins...
- and so, what if someone hacks into our wifi?
- maybe the network setup could help prevent attacks, e.g. by using allowlists of devices that can connect to certain devices, or separate subnets, or nets inside nets...
- do you see where this leads?
- paranoia!
- 💁🏼♀️
- paranoia!
- do you see where this leads?
- maybe the network setup could help prevent attacks, e.g. by using allowlists of devices that can connect to certain devices, or separate subnets, or nets inside nets...
- at the end of the day this is code written by someone else and running in my machine, do we trust it?
References
I used the advice in these page. None did exactly what I wanted, but they together gave me the info I needed!
- Installing nextcloud on the Raspberry Pi.
- Install Nextcloud on debian 12 - I used the advice to fine tune the PHP settings.
- Harden Nextcloud server - it's probably a good idea to do these or at least some of them.
- Create and use a self signed SSL certificate on Apache.