This guide assumes you already have a VPS setup and running and a Domain already setup on Cloudflare. If you're yet to select a VPS Consider using my referral link to support the blog and also earn yourself a $100 credit on Vultr!
Learn how to self host Gitlab on your own private VPS using Docker and Docker Compose. Configuring Cloudflared and protecting your Gitlab instance using Cloudflare Access on Cloudflare's Zero Trust platform
This tutorial assumes that you've already installed Docker and Docker compose on your VPS.
First lets create the Docker-compose file that will spin up our service -I like to put all my docker containers in the same folder.
cd mkdir stacks cd stacks nano docker-compose.yml
Create the folder to house the Docker-compose file
In the docker-compose.yml file use the following yaml to define the service we want to deploy
version: '3.7' services: web: hostname: lab.alexgallacher.com image: "gitlab/gitlab-ee:latest" environment: GITLAB_OMNIBUS_CONFIG: | gitlab_rails['initial_root_password'] = 'adminadmin' ports: - "80:80" - "2224:22" restart: always shm_size: 256m volumes: - "/home/<username>/stack_volumes/gitlab/config:/etc/gitlab" - "/home/<username>/stack_volumes/gitlab/logs:/var/log/gitlab" - "/home/<username>/stack_volumes/gitlab/data:/var/opt/gitlab"
Let's break down the Docker Compose file so we understand what's inside:
- We're setting the hostname as the url that you'll end up browsing to
- The image tells Docker that we want to pull the latest gitlab/gitlab-ee image
- The Environment contains the initial root password that's required to login to the Gitlab web interface as the latest version of Gitlab prevents additional user signup without administrator approval forcing us to have to set this when we deploy the container.
- We're mapping a number of Ports here 80:80 tells Docker to spin up the web interface on port 80 - this works for us as we don't need to deploy over HTTPS as our Cloudflared setup covers us for this. Port 2224:22 maps an external port of 2224 to port 22 for ssh. This port enables you to git push to your own Gitlab instance. It's worth pointing out the reason why we're mapping 2224 as opposed to 22 to port 22 in the container is that I'm running ssh through cloudflared to my VPS.
- There's three primary Volumes that we need to map to the host from the container for Gitlab to use. These are Config, Data and Logs. Depending on how you have setup your VPS your approach to storing data from containers may differ. For me, Under my /home/username/ folder I've created a folder called "stack_volumes/gitlab/ and duplicated the 3 folders - Config, Data and Logs. To do this yourself execute the following commands line by line. (Note: you may need to use Sudo here if it throws a permission error, it's entirely dependent on how you've setup and configured your users on your VPS):
cd home/<username> mkdir stack_volumes cd stack_volumes mkdir gitlab cd gitlab mkdir logs mkdir config mkdir data
Bash commands to create the folder structure I use to manage host to container volumes for Docker Compose
Before we spin up the Gitlab service let's configure Cloudflared and Cloudflare's DNS settings for our website.
This section of the tutorial assumes that you've configured Cloudflared as a service on your VPS, check out how to configure Cloudflared on Cloudflare or check out my previous blog around setting up Cloudflared for a secure Ghost blog
Let's go in and edit the cloudflared configuration file. Navigate over to the Cloudflared configuration file
Navigate to the Cloudflared Configuration file location
Let's go ahead and edit the file
edit the Cloudflared config.yml
💬 If there isn't a config.yml file in this location it's likely that you haven't deployed Cloudflared as Service on your VPS. To do this follow the Cloudflare tutorial on setting up Cloudflared as a service
let's go ahead and add two new hostnames and associated local service url's
- hostname: lab.alexgallacher.com service: http://localhost:80 - hostname: lab-ssh.alexgallacher.com service: ssh://localhost:22
Add additional cloudflared services
Let's explore what we've just added a bit further here:
- Cloudflared is redirecting requests for lab.alexgallacher.com to the localhost service running on port 80 and is also redirecting requests for lab-ssh.alexgallacher.com to a localhost service running port 22.
If you've managed to update the cloudflared config.yml file your configuration file should look something like this now:
You're going to now need to restart the Cloudflared service to apply the config.yml changes, you can do that through this quick command - note depending on the Linux distro you're using here, this command for you might be different. If you're struggling to find the right command you can simply reboot your VPS and the changes will be applied via 'sudo reboot'.
sudo systemctl restart cloudflared
Add additional cloudflared services
If you are using Cloudflared for SSH, you'll notice a temporary disconnect while the service restart - this is normal!
So we've updated Cloudflared to automatically redirect incoming traffic to lab.alexgallacher.com to the correct localhost service running within our VPS. next we need to actually instruct Cloudflare to forward and requests to lab.alexgallacher.com to our cloudflared service running on our VPS.
Browse to the DNS settings on your Cloudflare dashboard and add two new CNAME records, 1 for lab and one for lab-ssh that redirect to your cloudflared service ID. If you don't know what this you'll need to run through how to setup up Cloudflared on your VPS
The two DNS entries should look something like this when you're done:
Once you've setup the Gitlab Docker compose file, Cloudflared and configured the two CNAME records on your DNS records within Cloudflare you're now in a position to start up Gitlab for the first time.
💬 It's worth noting here that Gitlab is pretty intensive each time it's started. I had tried to spin it up on a 2gb and 2gb of Swap space but this caused timeout's when the container was rolling through the installation of all the recipes. to avoid this I recommend setting up least 4gb of swap space if your relatively limited on ram (<2GB).
let's cd back into the folder where we have the docker-compose.yml file located from before and spin up the service.
cd stacks docker-compose up
Browse to the folder where the docker-compose.yml configuration file is located and tell Docker to spin up the Docker-compose file.
This will spin up the service with you viewing the outputs from the container. If you want to detach from the container simply tag on -d
docker-compose up -d
Detach from the container
If anything goes wrong you can gracefully stop the container by commanding:
stop the containers deployed from the Docker compose file
💬 It's worth noting that it does take roughly 5-15 mins on the first run to download and extract the image and subsequently run all the installation of Gitlab within the container.
After 10-15 minutes you can browse directly to the url, in my case this was lab.alexgallacher.com. To login let's enter the credentials we created earlier in the Docker-compose.yml file.
In my case this is:
- username: root
- password: adminadmin
💬 If you're going to be using this in production please make sure you're using complex passwords. 'adminadmin' is for demonstration purposes only and should be used in a production environment for the root account!
Great, we've got Gitlab running. Next we need to use Cloudflare's Zero Trust technology to protect Gitlab.
Go ahead and and browse to Cloudflare Zero Trust. On the main page you'll want to browse to Access -> Applications and then click on add application.
We need to select Self Hosted as we're self hosting Gitlab
Add an application name. In my case i'm calling mine Gitlab. We need to map the DNS CNAME location under the Application domain. In my case this is lab.alexgallacher.com. We don't require a specific / optional path as we want to protect everything under the lab.alexgallacher.com domain. Note the Identity Provider section highlight's we're going to be using a One time PIN. In order to access the page the end user will need to validate a One-Time Pin with Cloudflare.
Once you're done click next.
The next section covers configuring access to the protected domain. In my case i'm going to create a simple policy to allow my personal email access to the domain via a One-time PIN. This means that when I enter this email, Cloudflare will validate that my email is allowed to be sent a PIN prior to sending it. Any other emails that are entered to the authentication page, outside of the rule will not be sent be authorised to be sent a PIN.
Make sure you replace myemail@emails with your own email!
I'm going to leave the CORS and Cookie settings blank to make this as simple as possible, but if you're using this in production, this should be filled out and aligned with broader organisational policies as these are rather important settings we're skipping over.
Once done, go ahead and click "Add Application"
Once Cloudflare access has been configured, go ahead and browse back to the url that you configured for Gitlab. You'll be presented by a Cloudflare protected Authentication page. Any attempt to browse to any page under the lab.alexgallacher domain without a browser access cookie from Cloudflare (Which is currently set to expire after 24 hours based on the policy we just defined) will redirect the user back to the Cloudflare Access Page.
After entering my email (Which is validated in our policy rule on Cloudflare as being authorised to receive OTP's) I get an email from Cloudflare:
If you click the link you'll be authenticated into the protected page for a period of 24 hours as defined in our policy.
Cloudflare's Zero Trust platform is incredibly versatile for those self hosting a number of the applications in house. Gitlab is a prime example. Check out how to protect a Ghost blog on my other article