Hello! My name is Nick and I'm going to show you how to use Kubernetes to self-host your own Nextcloud server.
While the primary purpose of this article is to give you a crash course in Kubernetes, I want to start by answering the question, “What is Nextcloud?” Well, Nextcloud is like Dropbox. It allows you to store files in the cloud, to share files with other people, and to collaborate with co-workers. Plus, it's free and open-source.
Source Code (Optional)
If you want to get your hands on the code, click on the Github link below. Be sure to
git checkout the
nextcloud tag to view the finished code. Note that in future posts, I will add additional projects to the repo and making changes that aren't reflected in this post.
Set Up Minikube and Kubectl
Since we are going to run Kubernetes on our local machine, I’m going to install Minikube, the single-node development cluster. If you don’t already have Minikube installed, go here: https://kubernetes.io/docs/tasks/tools/install-minikube
Make sure that you also install Kubectl, the command line utility for managing your Kubernetes cluster.
Once Minikube is installed, start it up using the command
Define the MySQL Deployment
Nextcloud is made up of two main components: a server container and a database container. Let's start by going over the database.
What we've got here is a config file for a Kubernetes Deployment named
The purpose of this deployment is to make sure that exactly one DB Pod is running at all times.
If the pod dies or gets evicted from its host node, the deployment will create a replacement pod.
Then what is a Kubernetes pod? Well, it’s basically a “home” for a container. In Kubernetes, containers always live inside of a pod. In this example, the pod is home to a container named
mysql which is created from the
mysql:5.7 Docker image.
So to recap: the MySQL DB runs in a container, that container runs in a pod, and the DB pod is created and managed by our deployment. And what do we get from all of this? Well, we get a DB which is more resilient than it otherwise would be.
So far so good. But this deployment config file is missing two important things: some MySQL environment variables, and a stable place to store the DB data. Let's start by going over the database issue.
Here's the problem: when a container terminates or crashes, everything stored in the container and it's file system is gone for good. Therefore, we don't want to store the database data on the
mysql container. So, we are going to store the data on a Kubernetes Volume. You see, volumes are designed to provide durable storage to containers.
Okay, here's how we set up the volume for the
mysql container. First we need a Persistent Volume Claim, otherwise known as a PVC.
This one requests 256Mi of persistent storage. The cool thing is that the details of “where” and “how” are completely abstract away from us. We don't need to worry about them.
Now let's add the volume configuration code to the
mysql container in the deployment config file.
In this bottom section under the
volumes field, we have what amounts to a volume declaration.
It's kind of like a variable declaration in C or Java, but instead of declaring a variable, we are declaring a volume named
persistentVolumeClaim field, we reference the
volumeMounts field specifies the directory where the volume gets mounted onto the container’s file system. This means that all files saved in
/var/lib/mysql are actually stored in our volume instead of on the container’s filesystem.
Environment Variables and Secrets
The config file is missing some key MySQL environment variables. Let’s start by declaring the
MYSQL_DATABASE=nextcloud environment variable. This sets the name of the DB.
Technically, we could use the same syntax to declare the
MYSQL_ROOT_PASSWORD. But this is a bad idea because Kubernetes config files are usually stored in a version control system. And you really shouldn’t store raw passwords and other sensitive data in your VCS.
The easiest way to create a secret object is by using the
kubectl create secret command. Here’s what that looks like:
kubectl create secret generic nextcloud-db-secret \ --from-literal=MYSQL_ROOT_PASSWORD=rootPassw0rd1 \ --from-literal=MYSQL_USER=nextcloud \ --from-literal=MYSQL_PASSWORD=passw0rd1
This creates a secret named
nextcloud-db-secret with three key-value pairs:
After we create the secret, we need to extract the key-value pairs from it and turn them into environment variables on our database container. Add the following code to the
mysql container to do just that.
Now our database deployment is ready to go.
Define the MySQL Service
We also need to define a Kubernetes Service to ensure reliable communications with the DB pod.
A service is a type of component which acts as a communications middleman between a pod and its clients. This way, a pod’s clients don't need to worry about whether they are talking with the original pod or a replacement pod with a different address.
This service sits in front of the pod(s) created by our deployment.
Any packets that the service receives on port
3306 will get sent to the DB pod’s port
3306. (In case you didn’t already know this,
3306 is the default MySQL port.)
Multiple YAML Documents, One File
Notice that the triple Dash line separates the deployment from the service.
These three dashes signify the start of a new YAML document. Thus, you can have multiple component config docs in a single file.
Create the MySQL Database Components
We are now ready to create the database components. For context, here are the files that we've been working with.
First, create the persistent volume claim:
kubectl apply -f nextcloud-shared-pvc.yaml
Second, create the DB deployment and service:
kubectl apply -f nextcloud-db.yaml
Note that while it will only take a second or two to create the deployment and service, the database probably won't be running yet. The deployment needs to create the DB pod, and the pod needs to create the DB container. But first, Kubernetes must download the
mysql:5.7 image from Docker Hub, and that can take a few minutes.
In other words, you'll need to be patient the first time you run these commands until the
mysql:5.7 image is cached by your cluster.
The Nextcloud Server Deployment
Now that the database is up and running, let's create the Kubernetes config files for the Nextcloud server components.
Here we have the Kubernetes deployment named
nextcloud-server As you can see from the
replicas field, this deployment makes sure that exactly one server pod is always running.
Notice that the container's
image field still needs to be filled in. Let’s fix that. I'm going to google “Docker Hub Nextcloud” to find the Nextcloud Docker Hub page.
The Nextcloud documentation says, “the
apache tag contains a full Nextcloud installation including an apache web server.” That's what we're looking for, so let's go with the
nextcloud:16-apache Docker image.
While Nextcloud does use our MySQL database to store stuff, some things are not stored in the DB--things like user-uploaded files and whatnot. Those files get saved in
/var/www/html. We need to mount a volume onto that directory to make sure files get persisted in case the container dies and gets recreated. Here’s the code to do all that:
Note that we are using the exact same volume backed by the same PVC as the MySQL database. This was just a design decision that I made to keep things simple.
The Service for the Server Deployment
We also need to create a service for our
Clients who want to talk to the server should instead communicate with the service. All packets that the service receives on port
80 will get forwarded to port
80 on a
Create the Nextcloud Server
In order to create the Nextcloud server deployment and service, simply run the following command in the same directory as before:
kubectl apply -f nextcloud-server.yaml
You might be wondering why we haven't defined any database-related environment variables.
Well, we are going to configure that stuff on the Nextcloud web page. What we need to do now is make the Nextcloud server browser-accessible.
By default, clients outside of the cluster cannot communicate with pods inside of the cluster. The way that we are going to solve this is by creating a Kubernetes Ingress object. It will allow external clients to connect with the Nextcloud server in our cluster. (It can do much more, but don’t worry about that right now.)
Okay. Here is our Ingress config file.
Let's assume that
files.mysite.test resolves to the IP address of our Minikube node--i.e. the one (virtual) machine in our development cluster. Then all HTTP requests addressed to
files.mysite.test/ will get routed to port
80 on the service named
Enable the Minikube Ingress Addon
Before we create the ingress object, we need to enable ingress on our cluster. You see, ingress is not enabled on Minikube by default.
Enable it using this command:
minikube addons enable ingress
Create the Ingress Object
In order to create the ingress object, we need to go to the directory containing the config file.
Then run the following command:
kubectl apply -f cluster-ingress.yaml
Update the "hosts" File
If we were to type
files.mysite.test/ into my browser, we’d get a 404 error. The request would not get routed to the Nextcloud server because that host name doesn't yet map to the IP address of our Minikube node.
Let's get the IP address of Minikube:
Then for test purposes only, I'm going to add the following entry into my computer's
Now we can visit the Nextcloud site in the browser!
Configure and Try Out Nextcloud
We are ready to visit the Nextcloud site and test things out. So let's visit
http://files.mysite.test in the browser.
We need to configure the site. Start by adding credentials for Nextcloud. I'm going to use admin/admin for simplicity.
The next step is to configure the server to use the MySQL database that we created for this project. So click on:
- Storage & database
- Fill in the database user and password with the values from the
- Fill in the database name with the value from the MYSQL_DATABASE environment variable in the MySQL deployment config document (see it).
localhostto the domain and port of the DB service. This ends up being <SERVICE NAME>:<SERVICE PORT> (see it).
- Click Finish setup.
The setup is complete, and you should see something like this:
I'm glad you made it all the way to the end! You now have you own locally-hosted Nextcloud server and a better idea of what working with Kubernetes looks like.