Sharing notes from my ongoing learning journey — what I build, break and understand along the way.
Build Your Own Private Cloud: Apache2, Virtual Hosts & Nextcloud Guide
Building a Private Intranet Cloud: A Step-by-Step Guide to Apache2, MariaDB, and Nextcloud
Hello everyone! In this post, I will share my adventure of building a fully-fledged web server from scratch in a “Remote Lab” environment. Our goal is not just to publish a simple web page, but to host multiple websites on the same server (Virtual Hosting) and finally, to take full control of our data by setting up our own private cloud system, Nextcloud.
In this project, we worked with three different machines:
- SRV1: Our DNS (BIND9) server that handles name resolution for our network.
- SRV2: Our main server where we installed our web services (Apache2, PHP, MariaDB, Nextcloud).
- CL01: Our client machine where we test our operations via a browser.
If you’re ready, open up your terminal windows; let’s get started!
Part 1: Laying the Foundation – Apache2 Web Server Installation
Every great setup starts with a solid web server. For this project, we chose Apache2, an industry standard. First, we need to connect to our SRV2 machine and install our web server.
We start the installation in the console with this command:
sudo apt install apache2 -y
The installation completes in seconds. But a system administrator’s first rule is “trust, but verify.” Therefore, we need to check if the service is actually running: sudo systemctl status apache2

When we see the green “active (running)” text in the output, we breathe a sigh of relief. This shows that Apache has successfully started in the background as a daemon. At the same time, the “enabled” output guarantees that this service will start automatically when the server is rebooted.
Okay, the service is running, but is it listening to network requests? To check this, we look at the status of our network sockets:
ss -tapn

In the table that appears on the screen, we see the “LISTEN” state at the 0.0.0.0:80 address. This means Apache2 is ready to handle all incoming requests over port 80, the traditional HTTP port.
Finally, to visually confirm that everything is fine, we switch to our CL01 (client) machine and type the IP address of SRV2 into our browser’s address bar.

We are greeted by the famous “Apache2 Debian Default Page”! We have successfully completed the first stage.
Part 2: Hosting Multiple Sites on a Single Server – Virtual Hosts
Our web server is running, but in the real world, a single server usually hosts multiple websites. This is called “Virtual Hosting”. In this section, we will create a custom website named lf10b.gfn.internal.
1. Creating the Directory and Content
First, we create a folder where our site’s files will reside:
sudo mkdir /var/www/lf10b
Then we enter this directory and create our site’s homepage. By typing nano /var/www/lf10b/index.html, we add a simple HTML structure inside. This will be our “dummy” page.

As you can see on the screen, we have prepared a page that welcomes users with the message “Herzlich Willkommen im Lernfeld 10b!” (Welcome to Learning Field 10b!) .
2. Apache Configuration
Now we need to tell Apache, “If someone asks you for the lf10b.gfn.internal address, show them the files in the folder I just created.” For this, we create a new configuration file in the /etc/apache2/sites-available/ directory:

In the contents of the file (lf10b.gfn.internal.conf), we specified our domain name as ServerName and the /var/www/lf10b/ directory as DocumentRoot. We also defined the locations where the error and access logs specific to this site will be kept.
3. Activating the Site and DNS Configuration
Writing the configuration file is not enough; we need to activate it:
sudo a2ensite lf10b.gfn.internal.conf And to apply the changes: sudo systemctl reload apache2

However, we have a small problem: Our client machine (CL01) does not yet know which IP the lf10b.gfn.internal address corresponds to. So we switch to our SRV1 machine (our DNS Server) and add the following line to our BIND9 zone file (/etc/bind/db.gfn.internal): lf10b IN A 192.168.10.13
Now everything is ready! When we go to our browser and call this address, a flawless process takes place:
- The browser goes to the DNS server (SRV1) and gets the IP address.
- It sends an HTTP request to the 80th port of SRV2.
- Apache looks at the FQDN (domain name) information in the incoming request, finds the correct vHost, and delivers the file to us.

Our page is on the screen! Using the same logic, we can easily host different sites like website1 and website2 on this server.
Part 3: Towards the Cloud – Nextcloud Infrastructure (PHP and MariaDB)
Nextcloud is a fantastic open-source platform that allows us to host our files, calendars, and contacts in a private cloud environment. Whether you use it in your private life, for your company, or for public administration, your data is completely under your control.
However, Nextcloud is a dynamic system. It needs PHP to run and a database to store its data.
1. Installation of PHP 8.2 and Its Extensions
Debian 12 comes with PHP 8.x packages, which are tailor-made for Nextcloud. With our installation command, we download PHP and many critical extensions that Nextcloud requires, such as curl, mysql, gd, xml, and zip.
After the installation is complete, we check the versions and extensions:

In the output, we see PHP version 8.x and everything we need in the modules list. Afterwards, we enter the /etc/php/8.2/apache2/php.ini file and adjust the memory limit (memory_limit = 512M), file upload limits, and time zone (Europe/Berlin) to increase Nextcloud’s performance. One of the most important steps was to enable PHP OPcache and enter the values recommended by Nextcloud (for example, opcache.memory_consumption = 128).
2. MariaDB Database Installation and Security
We use MariaDB as our database: sudo apt install -y mariadb-server

After seeing that the service is “active,” we secure our server with the sudo mariadb-secure-installation tool. Thanks to this tool, we set a root password, delete anonymous users (Remove anonymous users? Y), and close the doors to external threats by removing test databases.
Then we log into the MariaDB shell (mariadb -u root -p) and create a specific database (nextcloud_db) for Nextcloud and a user (nextclouduser) who will have full privileges on this database.
To confirm the accuracy of our operation, we run the SHOW GRANTS query:

In the table, we clearly see that our user was created successfully and has gained access rights to the correct database.
Part 4: The Finale – Downloading and Publishing Nextcloud
Our infrastructure is rock solid. Now we can download Nextcloud to our server.
(Note: Just before writing this article, we completed the process of downloading the latest version of Nextcloud using the curl tool and extracting it to the /var/www/nextcloud directory using unzip. In order for the data to be read and written by the Apache web server, we also adjusted the file ownership with the sudo chown -R www-data:www-data nextcloud command.)
1. vHost Settings for Nextcloud
Just like we did in Part 2, we need to create a virtual host (vHost) for Nextcloud. We create the /etc/apache2/sites-available/nextcloud.gfn.internal.conf file and insert the DocumentRoot /var/www/nextcloud/ and ServerName nextcloud.gfn.internal information into it.
We use Apache’s testing tool to see if we made any mistakes in the configuration: sudo apachectl configtest

The “Syntax OK” text that appears on the screen is proof that we wrote everything correctly! We activate the configuration (a2ensite) and reload Apache.
2. DNS Configuration and Web Installation
To be able to access our site by name on the client side, we open a new record on our SRV1 (DNS) server. But this time, instead of an “A” record, we add a “CNAME” (Alias) record pointing to our SRV2 server (nextcloud IN CNAME srv2).
Now we can open our browser and go to the http://nextcloud.gfn.internal address!
(Note: On the installation screen that greeted me when I first logged in, I set my admin username/password and entered the nextclouduser, nextcloud_db, and localhost information I created in MariaDB into the database settings section. Then, I approved the “Install recommended apps” option provided by the system, ensuring the automatic installation of critical apps like contacts, calendar, and mail.)
And here is the magnificent ending! After all this hard work, we reach the Nextcloud Dashboard:

Conclusion
With this project, we didn’t just run a few Linux commands. We practically saw how an HTTP request leaves the DNS server and reaches Apache vHosts, how PHP and MariaDB work in sync in the background, and how we can keep our data security within our own internal network (Intranet).
Managing your own cloud is an incredible feeling of freedom. I hope this detailed guide has been enlightening for anyone who wants to build their own server infrastructure.
