I write a lot of PowerShell scripts for application installations, and many download the install files from a URL. Because the URL is hardcoded in the scripts, if the URL changes, I need to update all the scripts. This exact issue happened to me a few years ago, and my solution at the time was to write a Python script to update the PowerShell scripts. You can read about that solution in my blog post, Script to make Scripts.
However, if my scripts had used a URL shortener instead, I could’ve edited the short URL to resolve the issue. The problem with most URL shorteners is that you can make short URLs all day long, but you can’t edit them, and if they let you edit them, it’s a paid feature, and they also start limiting how many hits your short URL can get. I am not a fan of any of that.
Fortunately, a URL shortener named Shlink exists. Shlink is a self-hosted open-source URL shortener. With Shlink, you can do whatever you want with your short URL, including editing them.
Once I got Shlink set up, I re-wrote my PowerShell scripts to use my Shlink short URLs. Now, if an install URL changes, I can edit the Shlink short URL, and I don’t need to update my PowerShell scripts.
In this post, I will show you step-by-step how to set up Shlink with Docker and Cloudflare Tunnels.
The Shlink setup will use a Cloudflare tunnel for external access and a MariaDB database for the Shlink database. I selected MariaDB as I am more familiar with it. However, Shlink does support other databases. We will also use the Shlink web client as a UI to easily manage the short links.
Prerequisites
- Domain for the short links.
- DNS for the domain hosted in Cloudflare.
- Docker host.
The Process
- Make a folder to store your docker configurations for Shlink.
I’ll call mine shlink.
- Download the
docker-compose.yml
and thesample.env
files from my GitHub https://github.com/thedxt/Shlink-docker and place them in the folder.
.env
Setup
- Copy the
sample.env
file as a new.env
file. - You will need to define the variables in the
.env
file. - The variables are:
CONTAINER_NAME
is the name of your Shlink stack. There will be 4 containers spawned.- The one with
_app
appended to it is the Shlink application image that runs all of Shlink. - The one with
_db
appended to it is the MariaDB image for Shlink to store all the configurations. - The one with
_web_client
append to it is the Shlink web client image that gives you a nice UI to manage your short URLs. - The one with
_cf
appended to it is the Cloudflare tunnel image for the Shlink application.
- The one with
DB_NAME
is the name of the MariaDB database that Shlink will use.DB_ROOT_PWD
is the MariaDB root password.DB_USER
is the database user that Shlink will use.DB_USER_PWD
is the password for the database user that Shlink will use.SHORT_DOMAIN
is the domain you will be using for your short URLs. (only include the FQDN (Fully Qualified Domain Name))REDIRECT_DOMAIN
is where invalid short links, direct visits to the short domain, or 404 pages will be sent. (this needs to start with https://)APP_PORT
is the port that the Shlink application will run on and the port you will use with the Cloudflare tunnel.ADMIN_API
is the initial API key that will be used to configure your Shlink and will be preloaded to the Shlink Web Client.WEB_CLIENT_PORT
is the port the Shlink web client will use.CF_TUNNEL_B64
is the Base64 of your Cloudflare tunnel for Shlink.
For example, I will use the domain dxt.zip as the primary domain for Shlink. I will be redirecting all non-short URL traffic to my blog, thedxt.ca. I will use port 8788 for the Shlink application and port 8787 for the Shlink web client to manage Shlink.
Here is an example of what my .env
file looks like.
CONTAINER_NAME=DXT-SHLINK
DB_NAME=shlink
DB_ROOT_PWD=KFHDFh2hjshd34jsdfiuwjkls4dfjhk9
DB_USER=shlink_user
DB_USER_PWD=dfgjkhjF3DSH2FSDJlkj42dfdjklf
SHORT_DOMAIN=dxt.zip
REDIRECT_DOMAIN=https://thedxt.ca
APP_PORT=8788
ADMIN_API=sjkhfdDFJFKD378kfhjsdSdshs433
WEB_CLIENT_PORT=8787
CF_TUNNEL_B64=
Code language: plaintext (plaintext)
Cloudflare Tunnel Setup
For more details on how to set up a Cloudflare tunnel, my blog post, Cloudflare Tunnel with Docker, goes into more detail.
- Log in to Cloudflare and create a tunnel.
- Select Cloudflared.
- Give the tunnel a name.
I will use the name DXT-ZIP-SHLINK.
- Copy the Base64 from the install command and click Next.
Example Cloudflare tunnel Base64
eyJhIjoiTXlBY2NvdW50VGFnMTIzIiwidCI6Ik15VHVubmVsSUQxMjMiLCJzIjoiTXlUdW5uZWxTZWNyZXQxMjMifQ
Code language: plaintext (plaintext)
Save the Cloudflare tunnel Base64 as the variable CF_TUNNEL_B64=
in your .env
file.
- Select the domain you will be using with Shlink.
I will be using dxt.zip.
- For service, select http and enter the URL of the docker host that will run Shlink, followed by the port for the Shlink App.
I will use the Shlink app on a docker host running on 192.168.172.19 on port 8788.
Shlink Setup
With the Cloudflare tunnel configured, your .env
file should look something like this now.
CONTAINER_NAME=DXT-SHLINK
DB_NAME=shlink
DB_ROOT_PWD=KFHDFh2hjshd34jsdfiuwjkls4dfjhk9
DB_USER=shlink_user
DB_USER_PWD=dfgjkhjF3DSH2FSDJlkj42dfdjklf
SHORT_DOMAIN=dxt.zip
REDIRECT_DOMAIN=https://thedxt.ca
APP_PORT=8788
ADMIN_API=sjkhfdDFJFKD378kfhjsdSdshs433
WEB_CLIENT_PORT=8787
CF_TUNNEL_B64=eyJhIjoiTXlBY2NvdW50VGFnMTIzIiwidCI6Ik15VHVubmVsSUQxMjMiLCJzIjoiTXlUdW5uZWxTZWNyZXQxMjMifQ
Code language: plaintext (plaintext)
- Run
docker compose up -d
to start everything and create the containers and the volume for the database.
If we go to the IP of the docker host with port 8787, we can access the Shlink web client.
The Shlink setup uses the ADMIN_API=
variable to preload the Shlink web client with the API key needed to access Shlink.
I wouldn’t expose your Shlink web client to the internet because it automatically has access to your Shlink due to the preloading of the API key. The goal should be to use the Shlink web client internally to manage your Shlink.
If you want to manage your Shlink externally, you could use the free web client that Shlink themselves hosts on app.shlink.io.
Once you log in to the Shlink web client, you can now create your own short links.
Testing
First, make sure the Cloudflare tunnel is up by checking the Cloudflare tunnel dashboard.
It’s a bit hard to illustrate that Shlink is working because it just forwards you along and works. However, curl can help.
If we curl the root Shlink domain dxt.zip, we get nothing, but that’s because curl doesn’t follow redirects by default.
If we run curl with the -L
option, curl will now follow redirects. Because the examples I am using are full web pages, I will also use the -s
option to silence the progress meter and pipe the output to grep to show only the web page’s title as a simple example.
Now, if we run curl on the root Shlink domain dxt.zip curl -s -L dxt.zip | grep '<title>'
we see that the title is theDXT.
If we run the same curl command for thedxt.ca curl -s -L thedxt.ca | grep '<title>'
we can see that the title is also theDXT, which shows that raw visits to dxt.zip are redirected to thedxt.ca.
If we curl the URL of my blog post about Cloudflare tunnels with Docker we see that the title is Cloudflare Tunnel with Docker.
I will create a short link for that post using Shlink. The short link is https://dxt.zip/DVW93.
If we curl just the short link without the -L
option, the output is nothing.
If we add back in -L
we can see that Shlink forwarded us to the blog post.
Because Shlink is self-hosted, we control everything. I’m going to edit the short link to go to another post.
If we curl the same short link, Shlink sends us to a different blog post.
Summary
With the power of the self-hosted URL shortener Shlink, the possibilities are endless. My use of Shlink hardly scratches the surface of the available features, but that’s ok. It works perfectly for what I need it for. It blows my mind that it works so well and is entirely free and open-source.
I’ve been using Shlink for a few years, and it has been rock solid. I think the version I first started playing with was version 2. Development is very active with Shlink. They are currently on version 4.
Now, all my PowerShell install scripts use short links from Shlink, and if I need to change a URL, I can do so without needing to edit my scripts.
If you want to check out all the features of Shlink, here is their list of features. If you want to read more about Shlink, here is their documentation.
If you want to read more about Cloudflare tunnels, here is their documentation.