How to Host Your Own bit.ly Server with Shlink in Linux

A photograph of a laptop on top of a table.

Shlink is a powerful self-host link shortener service for Linux. It provides a simple, yet effective framework where you can use your own domain name as the root for your shortened links. This article will show you how to install your own Shlink instance on Ubuntu Linux using Docker and Nginx.

One of the biggest selling points of Shlink is that you can use your own custom domain for shortlinks while still being self-hostable. This, in turn, gives you the flexibility of a SaaS link shortener service as well as the privacy of running software on your own machine.

A screenshot showing Shlink running on a demo server.

Aside from that, Shlink also boasts a diverse set of features such as the ability to create QR codes, automatically forward link queries, and create multi-segment custom slugs. This makes Shlink a handy tool if you’re looking for a link shortener service in Linux where you can tweak how your links look and behave.

Good to know: learn more about self-hosting by creating your own email aliases server with SimpleLogin.

Obtaining Docker for Shlink

Assumption: This article assumes that you’re installing Shlink on an Ubuntu 22.04 VPS and that you currently own a domain name.

The first step in deploying Shlink on Linux is to obtain a copy of Docker. To do this, fetch the Docker project’s signing key from their website:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

Create the repository file for your Docker binaries:

sudo nano /etc/apt/sources.list.d/docker.list

Paste the following line of code inside your new repository file:

deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable

Reload your system’s repository listing by doing an apt update:

sudo apt update && sudo apt upgrade

Installing the Dependencies for Shlink

With the Docker repositories up and running, you can now use apt to obtain the dependencies for Shlink:

sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin nginx

Ensure that the “core” snap package is available in your system:

sudo snap install core

Fetch and install the “certbot” snap package from the Electronic Frontier Foundation:

sudo snap install certbot --classic

Obtaining a Geolite2 License Key

Another defining feature of Shlink is that it can track the general location of the user who clicked on your links. To use this, open the Geolite2 Licensing Sign Up page.

A screenshot showing the sign up page for the GeoLite license.

Fill in the form with your details, then press Continue.

Open your email inbox, then look for the confirmation email from MaxMind.

Click the link from the email, then provide a password for your new MaxMind account.

A screenshot showing the password creation link for MaxMind.

Login to your MaxMind account, then click Manage License Keys on your Dashboard.

A screenshot highlighting the "Manage License Keys" menu item on the dashboard left sidebar.

Click Generate new license key, then click Confirm to create your Geolite2 key.

A screenshot highlighting the "Confirm" button on the License Key enrollment prompt.

Copy the Geolite2 license key to a text file on your machine then save it.

To install Shlink, you need to first create a virtual Docker network. This will serve as the app’s private network where it can communicate with the other containers that you will deploy:

sudo docker network create -d bridge \
    --subnet=10.0.0.0/24 \
    --gateway=10.0.0.1 \
    shlink-network

Deploy a PostgreSQL database and link it to your new virtual Docker network:

sudo docker run -d \
    --name shlink-db \
    -e POSTGRES_PASSWORD=!REPLACE_ME! \
    -e POSTGRES_USER=postgres \
    -e POSTGRES_DB=shlink \
    -p 127.0.0.1:5432:5432 \
    --restart always \
    --network="shlink-network" \
    postgres:12.1

Change the value of the “POSTGRES_PASSWORD” environment variable with a random string of text.

A terminal showing a random PostgreSQL DB password.

Note: You can create this random string of text by using “/dev/urandom”: cat /dev/urandom | tr -dc 'A-Za-z0-9' | fold -w 32 | head -n 1

Paste the following command to a new terminal session:

sudo docker run -d \
    --name shlink-stable \
    -p 8081:8080 \
    -e DEFAULT_DOMAIN=YOUR-ROOT.DOMAIN \
    -e IS_HTTPS_ENABLED=true \
    -e GEOLITE_LICENSE_KEY=!REPLACE_ME! \
    -e DB_DRIVER=postgres \
    -e DB_USER=postgres \
    -e DB_PASSWORD=!REPLACE_ME! \
    -e DB_HOST=shlink-db \
    --network="shlink-network" \
    shlinkio/shlink:stable

Replace the value of the DEFAULT_DOMAIN variable with your domain name.

A terminal showing the default domain value for the demo server.

Note: Make sure that your domain has an A record that points to your machine’s IP address.

Change the value of the “GEOLITE_LICENSE_KEY” variable with your personal license key.

A terminal showing the Geolite License for the demo Shlink server.

Replace the “DB_PASSWORD” variable with your PostgreSQL database password.

A terminal showing the PostegreSQL DB password for the demo server.

Press Enter to start your Shlink instance.

Installing and Linking Your Shlink Interface

Fetch an API key from your Shlink container, then copy it to your clipboard:

sudo docker exec -it shlink-stable shlink api-key:generate

Paste the following command to a new terminal session:

sudo docker run -d \
    --name shlink-client \
    -p 8080:8080 \
    -e SHLINK_SERVER_URL=https://YOUR-ROOT.DOMAIN \
    -e SHLINK_SERVER_API_KEY=!REPLACE-ME! \
    --network="shlink-network" shlinkio/shlink-web-client

Replace the “SHLINK_SERVER_URL” with the value from your “DEFAULT_DOMAIN” variable on the backend container.

A terminal showing the complete URL for the Shlink backend instance.

Paste your API key as the value for the “SHLINK_SERVER_API_KEY.”

A terminal showing the Shlink backend API key.

Press Enter to start your Shlink frontend interface.

Confirm that your database, Shlink backend, and Shlink frontend are working properly by listing all the currently active containers in the system:

sudo docker container list
A terminal showing the three containers that build the complete Shlink web app.

Creating an SSL Reverse Proxy using Nginx

At this point you now have a working Shlink software suite running on your server. In order to access it, however, you still need to create an SSL web proxy that delegates your two containers on different domains.

To start, create a new A record for your Shlink’s frontend interface. In my case, I will set my frontend’s A record as “admin.”

A screenshot showing the two DNS records for the demo Shlink server.

Create the site config file for your Shlink instance using your favorite text editor:

sudo nano /etc/nginx/sites-available/shlink

Paste the following block of code inside your config file:

server {
 
        server_name YOUR-ROOT.DOMAIN;
 
        location / {
                proxy_pass http://127.0.0.1:8081;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
        }
}
 
server {
 
        server_name SUBDOMAIN.YOUR-ROOT.DOMAIN;
 
        location / {
                proxy_pass http://127.0.0.1:8080;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
        }
}

Replace the “YOUR-ROOT.DOMAIN” variable with your root domain, and the “SUBDOMAIN.YOUR-ROOT.DOMAIN” with the subdomain for your frontend interface.

Save your config file, then enable your web app in Nginx:

sudo ln -s /etc/nginx/sites-available/shlink /etc/nginx/sites-enabled/

Reload your Nginx daemon to apply your new settings:

sudo systemctl reload nginx

Register your server to the Electronic Frontier Foundation:

sudo certbot register --agree-tos -m YOUR@EMAIL.ADDRESS

Request a new multi-domain SSL certificate for your Shlink instance:

sudo certbot --nginx -d YOUR-ROOT.DOMAIN -d SUBDOMAIN.YOUR-ROOT.DOMAIN

Confirm that your Shlink server is working properly by navigating to your instance’s admin console. In my case, it’s “https://admin.helloserver.top.”

A screenshot showing the default landing page for a Shlink instance.

Good to know: learn more about web encryption by creating your own SSL certificates using OpenSSL.

To use your new instance, click the Shlink menu item on the landing page.

A screenshot showing the Shlink menu item on the instance's landing page.

Paste your long URL on the URL to be shortened textbox under the Create a short URL category.

Provide some metadata for your short link, then click Save to create your link.

A screenshot showing the form for creating a short link in Shlink.

Open your new short URL on a new browser tab, then click the Visits button on your Shlink dashboard to test if it’s working properly.

A screenshot showing a basic analytics of the sample link on Shlink.

Installing and deploying your own link-shortening service is just the first step in taking back your online privacy. Learn how Linux can help you further reclaim your privacy by hosting your own anonymous website in Ubuntu using Tor.

Image credit: Carlos Muza via Unsplash and Shlink.io Developers. All alterations and screenshots by Ramces Red.

Subscribe to our newsletter!

Our latest tutorials delivered straight to your inbox

Ramces Red
Ramces Red - Staff Writer

Ramces is a technology writer that lived with computers all his life. A prolific reader and a student of Anthropology, he is an eccentric character that writes articles about Linux and anything *nix.