Have you dreamt of owning your own personal cloud storage that you can share with your family or friends? Or do you have a company which needs to have a storage cloud? If so, read along on how to setup the perfect cloud server for free at very affordable price. I have used BuyVM’s KVM service and their storage block for this tutorial. No, I am not getting paid by them, and this is not a sponsored post.

Get a KVM based VPS from BuyVM here. A 512 GB RAM KVM slice costs only $3 per month. Next get a block storage slab for this VPS here. You can get the cheapest (256GB storage) for $1.25 per month. You need to attach this slab to the VPS through their Stallion control panel.

Once this is done, we need to setup the partition table for the storage device just like we setup a new hard disk.

First create a new partition table:

fdisk /dev/sda
Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0x09329ff3.
Command (m for help): m
   a   toggle a bootable flag
   b   edit nested BSD disklabel
   c   toggle the dos compatibility flag
   d   delete a partition
   F   list free unpartitioned space
   l   list known partition types
   n   add a new partition
   p   print the partition table
   t   change a partition type
   v   verify the partition table
   i   print information about a partition
   m   print this menu
   u   change display/entry units
   x   extra functionality (experts only)
   I   load disk layout from sfdisk script file
   O   dump disk layout to sfdisk script file
  Save & Exit
   w   write table to disk and exit
   q   quit without saving changes
  Create a new label
   g   create a new empty GPT partition table
   G   create a new empty SGI (IRIX) partition table
   o   create a new empty DOS partition table
   s   create a new empty Sun partition table

We will now create a new partition in the new disk:

Command (m for help): p
Disk /dev/sda: 256 GiB, 274877906944 bytes, 536870912 sectors
Disk model: SLAB            
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x09329ff3
Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 
First sector (2048-536870911, default 2048): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-536870911, default 536870911): 
Created a new partition 1 of type 'Linux' and of size 256 GiB.
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Once partition table is written, one needs to format it. I am formatting it as an ext4 partition:

mkfs -t ext4 /dev/sda1
mke2fs 1.45.5 (07-Jan-2020)
Discarding device blocks: done                            
Creating filesystem with 67108608 4k blocks and 16777216 inodes
Filesystem UUID: b55b7108-e95d-45a6-a103-c388ea70694b
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
	4096000, 7962624, 11239424, 20480000, 23887872
Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done    

Now, we need to mount it. Let’s create a location to mount it in.

# mkdir /media/drive
# mount -t auto /dev/sda1 /media/drive 

Let’s add an entry to fstab so that our disk is automatically mounted at boot.

First create a backup of fstab:

cp /etc/fstab ./fstab.bak

Get the block id of our drive. We need to enter it in fstab file.

# blkid
/dev/vda2: UUID="6a91065c-1929-4ac0-9d83-5f1aeac133a5" TYPE="swap" PARTUUID="6346c738-02"
/dev/vda1: UUID="3d953e84-118a-45ba-ae28-99900ce35b9c" TYPE="ext4" PARTUUID="6346c738-01"
/dev/sda1: UUID="b55b7108-e95d-45a6-a103-c388ea70694b" TYPE="ext4" PARTUUID="09329ff3-01"

Copy the initial part from UUID upto ext4 into clipboard. Now edit the fstab file:

nano /etc/fstab

You can see the entry I created for my drive:

# /etc/fstab: static file system information.
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/vda1 during installation
UUID=3d953e84-118a-45ba-ae28-99900ce35b9c /               ext4    errors=remount-ro 0       1
# swap was on /dev/vda2 during installation
UUID=6a91065c-1929-4ac0-9d83-5f1aeac133a5 none            swap    sw              0       0
UUID=b55b7108-e95d-45a6-a103-c388ea70694b /media/drive	  ext4	  rw

Now we can restart and check that our drive was auto mounted

Install a cloud application on our server

Now that we have a VPS and storage device, let us create a sharing cloud application. I have chosen Seafile. You can use other options like Owncloud (I think they changed the name).

Installing seafile.

First, you need some basic dependencies to install seafile.

apt install build-essential python-dev python3-dev

Get Seafile from their site. Check the instructions given on their github. You can get the version number that you need to provide (like 8.0.5 below), from the latest available version as seen on this page.

wget --no-check-certificate https://raw.githubusercontent.com/haiwen/seafile-server-installer/master/seafile-8.0_ubuntu
bash seafile-8.0_ubuntu 8.0.5

Now installation starts:

 This script installs the community edition of the Seafile Server on a Ubuntu 16.04 (Xenial) 64bit
  - Newest Seafile server version, MariaDB, Memcached, NGINX -
  This installer is meant to run on a freshly installed machine
  only. If you run it on a production server things can and
  probably will go terribly wrong and you will lose valuable
  For questions or suggestions please contact us at
  [email protected]
  Possible options:
  1 = Seafile Community (Free) Edition (CE)
  2 = Seafile Professional Edition (PRO)
1) CE
2) PRO
Which Seafile version would you like to install? 1

I chose CE for community edition. PRO is of course a paid version. Once you hit Return, a long installation process will be underway.

If all went well, you will get the following message:

Your Seafile server is installed
  Server Address:
  Seafile Admin:       [email protected]
  Admin Password:      mypass
  Seafile Data Dir:    /opt/seafile/seafile-data
  Seafile DB Credentials:  Check /opt/seafile.my.cnf
  Root DB Credentials:     Check /root/.my.cnf
  This report is also saved to /opt/seafile/aio_seafile-server.log
  Next you should manually complete the following steps
  1) Log in to Seafile and configure your server domain via the system
     admin area if applicable.
  2) If this server is behind a firewall, you need to ensure that
     tcp port 80 is open.
  3) Seahub tries to send emails via the local server. Install and
     configure Postfix for this to work or
     check https://manual.seafile.com/config/sending_email.html
     for instructions on how to use an existing email account via SMTP.
  Optional steps
  1) Check seahub_settings.py and customize it to fit your needs. Consult
     http://manual.seafile.com/config/seahub_settings_py.html for possible switches.
  2) Setup NGINX with official SSL certificate, we suggest you use Let’s Encrypt. Check
  3) Secure server with iptables based firewall. For instance: UFW or shorewall
  4) Harden system with port knocking, fail2ban, etc.
  5) Enable unattended installation of security updates. Check
     https://wiki.Ubuntu.org/UnattendedUpgrades for details.
  6) Implement a backup routine for your Seafile server.
  7) Update NGINX worker processes to reflect the number of CPU cores.
  Seafile support options
  For free community support visit:   https://forum.seafile.com
  For paid commercial support visit:  https://seafile.com

You will now want to set up a DNS to point to this IP. Add an A entry on your DNS provider site. Mine is as always, Cloudflare, since it provides free Basic DDOS protection.

Now, you need to add your site’s hostname to your nginx configuration. You will be modifying the server_name value from seafile.example.org to your site DNS.

nano /etc/nginx/sites-enabled/seafile.conf
service nginx restart

On logging in, you will notice that the browser will warn about security of the site. So let’s next install an SSL certificate.

You are supposed to get updated info here. Since the script has installed seafile with nginx for me, I will be using it. However I found that the instructions for letsencrypt were outdated. So the method I followed was:

apt install snapd
sudo snap install core; sudo snap refresh core
snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: cloud.mysite.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1
Requesting a certificate for cloud.drjoel.in
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/cloud.mysite.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/cloud.mysite.com/privkey.pem
This certificate expires on 2021-10-07.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for cloud.mysite.com to /etc/nginx/sites-enabled/seafile.conf
Congratulations! You have successfully enabled HTTPS on https://cloud.drjoel.in
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

When you add your DNS and certificate, also remember to change the url settings:

cd /opt/seafile/conf/
nano seahub_settings.py
SITE_BASE                           = 'https://cloud.mysite.com'
FILE_SERVER_ROOT                    = 'https://cloud.mysite.com/seafhttp'

On restarting nginx, normally it should start working. It didnt for me, because of a port conflict. On checking:

2021/07/10 01:55:47 [emerg] 14903#14903: bind() to failed (98: Address already in use)
2021/07/10 01:55:47 [emerg] 14903#14903: still could not bind()

Hmm.. Apparently there was a port conflict for the port 443. Let me see what service is currently on port 443.

netstat -lpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 *               LISTEN      507/python3         
tcp        0      0 *               LISTEN      507/python3         
tcp        0      0*               LISTEN      10475/mysqld        
tcp        0      0 *               LISTEN      507/python3         
tcp        0      0*               LISTEN      4467/memcached      
tcp        0      0 *               LISTEN      507/python3         
tcp        0      0 *               LISTEN      507/python3         
tcp        0      0 *               LISTEN      507/python3         
tcp        0      0   *               LISTEN      507/python3         
tcp        0      0  *               LISTEN      11424/python3       
tcp        0      0  *               LISTEN      11378/seaf-server   
tcp        0      0 *               LISTEN      317/systemd-resolve 
tcp        0      0    *               LISTEN      417/sshd: /usr/sbin 
tcp        0      0   *               LISTEN      533/openvpn-openssl 
tcp        0      0*               LISTEN      11412/python3       
tcp6       0      0 :::22                   :::*                    LISTEN      417/sshd: /usr/sbin 
udp        0      0  *                           469/openvpn         
udp        0      0 *                           317/systemd-resolve 
udp        0      0*                           304/systemd-network 
raw6       0      0 :::58                   :::*                    7           304/systemd-network 

Oops.. openvpn-openssl is currently on port 443. That’s my VPN. So I needed to change its port. Changing the port of openvpn did not work. I found some documentation here.

So, I temporarily stopped openvpn service, and then restarted nginx and it worked:

service openvpn stop
service nginx restart

Oddly, on restarting openvpn now, that also worked!

service openvpn start

Now, that our certificate has been installed, we can access it at https://cloud.mysite.com without the browser warning.