PySerial minimal example: Copy data received from serial port to stdout

This example does not send data to the serial port but only copies data received from the serial port to stdout. Newlines received from the serial port are preserved.

#!/usr/bin/env python3
import serial
ser = serial.Serial("/dev/ttyUSB0", baudrate=115200)

    while True:
        response =
        if response:
            print(response.decode("iso-8859-1"), end="")

By using iso-8859-1   decoding, we ensure that even binary bytes are decoded in some way and do not cause an exception.

Posted by Uli Köhler in 3D printing, Embedded, Python

How to leave netmaker network

Once you have used netclient join to join a netmaker network, you can use

netclient leave --network mynetwork

to permanently leave the network named mynetwork (until you re-join of course).


Posted by Uli Köhler in Networking, VPN

How to load & start EMQX plugin using emqx_ctl

You can easily load (& start) an EMQX plugin such as emqx_auth_mnesia

./bin/emqx_ctl plugins load emqx_auth_mnesia

If you are using docker-compose and your container is named emqx, you can use

docker-compose exec ./bin/emqx_ctl plugins load emqx_auth_mnesia

After running this command, the plugin will show up as Running in the dashboard:

Posted by Uli Köhler in EMQX, MQTT

How to change EMQX admin password

Changing the password is as simple as

./bin/emqx_ctl admins passwd admin "[new password]"

If you are using docker-compose and your container is named emqx, you can use

docker-compose exec ./bin/emqx_ctl admins passwd admin "[new password]"
Posted by Uli Köhler in EMQX, MQTT

What is the EMQX default username & password

The default credentials for EMQX are:

  • Username: admin
  • Password:public

It should go without saying that you must change the password!

Posted by Uli Köhler in EMQX, MQTT

What is the EMQX default password for the ‘admin’ account?

The default password for the EMQX admin account is publicChange it as soon as possible 🙂

Posted by Uli Köhler in EMQX, MQTT

How to run emqx_ctl for docker-compose-based EMQX setups

You can simply run emqx_ctl using the following command:

docker-compose exec emqx ./bin/emqx_ctl [command]

where emqx is the name of the container to run emqx_ctl in.


docker-compose exec emqx ./bin/emqx_ctl status


Posted by Uli Köhler in EMQX, MQTT

How to fix docker emqx_ctl Node ’[email protected]’ not responding to pings.


When trying to run emqx_ctl in a dockerized emqx setup using a command like

docker-compose exec emqx ./bin/emqx status

you see an error message like

Node '[email protected]' not responding to pings.
/opt/emqx/bin/emqx: line 41: die: command not found


The problem here is that emqx_ctl is trying to connect to the IP address for but that IP address does not point to the IP address for the docker container (maybe it’s the public IP address for your server?)

The solution here is to create a network alias within docker/docker-compose so that the Docker DNS system resolves to the internal IP address of the container.

For example, in docker-compose, you can create your network using

    driver: bridge

and then configure the alias for the container using

    image: emqx:4.4.4
      - "EMQX_NAME=emqx"
      - ""
      - "EMQX_LOADED_PLUGINS=emqx_recon,emqx_retainer,emqx_management,emqx_dashboard"
      - 18083:18083
      - 1883:1883
      - ./emqx_data:/opt/emqx/data
      - ./emqx_log:/opt/emqx/log
          - ""



Posted by Uli Köhler in Container, Docker, EMQX, MQTT

How to use custom port for ZeroTier instead of 9993 on Linux

You can create /var/lib/zerotier-one/local.conf – by default it doesn’t exist, if it does, just add these settings:

  "settings": {
    "primaryPort": 9994


Posted by Uli Köhler in Linux, Networking, ZeroTier

How I fixed zerotier-one: fatal error: cannot bind to local control interface port 9993


On my server, which was running zerotier already using docker, I wanted to install zerotier on a system level.

but whenever I ran zerotier-cli I saw the following error message:

zerotier-one: fatal error: cannot bind to local control interface port 9993

and when I tried to start the daemon using zerotier-one -d this error message was displayed:

zerotier-cli: missing port and zerotier-one.port not found in /var/lib/zerotier-one


In my case, this was due to the standard port 9993 already being used by the dockerized zerotier. Therefore I needed to configure a custom port by creating /var/lib/zerotier-one/local.conf:

  "settings": {
    "primaryPort": 9994

You can choose any port you like.

Posted by Uli Köhler in Networking, ZeroTier

How to install gcsfuse on Ubuntu 22.04 (jammy)

If you try to install gcsfuse on Ubuntu 22.04, you will face two issues. First, the signature can’t be verified due to the new method of managing APT keys. Secondly, the gcsfuse repo does not have packages for Ubuntu 22.04 even 5 months after its release:

Err:10 gcsfuse-jammy Release                                                                                                                  
  404  Not Found [IP: 80]

You can work around both issues by force-installing the bionic packages which appear to work fine for me.

export GCSFUSE_REPO=gcsfuse-bionic
echo "deb [signed-by=/usr/share/keyrings/gcsfuse.gpg] $GCSFUSE_REPO main" | sudo tee /etc/apt/sources.list.d/gcsfuse.list
wget -qO- | sudo gpg --dearmor --yes --output /usr/share/keyrings/gcsfuse.gpg


Posted by Uli Köhler in Linux

How to fix apt error: NO_PUBKEY FEEA9169307EA071 NO_PUBKEY 8B57C5C2836F4BEB


When running apt update, you see the following error message:

Err:12 gcsfuse-bionic InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY FEEA9169307EA071 NO_PUBKEY 8B57C5C2836F4BEB


This error occurs due to the VirtualBox repo using the outdated (as of April 2022) apt-key method of importing keys.

You can import the key by just installing the gcsfuse repo again. The following commands are based on the official installation guide.

export GCSFUSE_REPO=gcsfuse-`lsb_release -c -s`
echo "deb [signed-by=/usr/share/keyrings/gcsfuse.gpg] $GCSFUSE_REPO main" | sudo tee /etc/apt/sources.list.d/gcsfuse.list
wget -qO- | sudo gpg --dearmor --yes --output /usr/share/keyrings/gcsfuse.gpg

and then running

apt update

again. Note that at the time of writing this post, gcsfuse is not available for Ubuntu 22.04 in the official repository.

You can work around this by installing the bionic packages instead of jammy. For more details, see our post

Posted by Uli Köhler in Linux

Coyping a folder and renaming file extensions in Python

This script copies myfolder to myfolder2 recursively and renames all .txt files to .xml

#!/usr/bin/env python3
import os
import os.path
import re
import shutil

srcfolder = "myfolder"
dstfolder = "myfolder2"

for subdir, dirs, files in os.walk(srcfolder):
    for file in files:
        # Rename .txt to .xml
        if file.endswith(".txt"):
            dstfile_name = re.sub(r"\.txt$", ".xml", file) # Regex to replace .txt only at the end of the string
            dstfile_name = file
        # Compute destination path
        srcpath = os.path.join(subdir, file)
        dstpath_relative_to_outdir = os.path.relpath(os.path.join(subdir, dstfile_name), start=srcfolder)
        dstpath = os.path.join(dstfolder, dstpath_relative_to_outdir)
        # Create directory if not exists
        os.makedirs(os.path.dirname(dstpath), exist_ok=True)
        # Copy file (copy2 preserves metadata)
        shutil.copy2(srcpath, dstpath)


Posted by Uli Köhler in Python

How to zip folder recursively using Python (zipfile)

This script will zip the folder myfolder recursively to myzip. Note that empty directories will not be copied over to the ZIP.

#!/usr/bin/env python3
import zipfile
import os

# This folder 
folder = "myfolder"

with zipfile.ZipFile(f"{folder}.zip", "w") as outzip:
    for subdir, dirs, files in os.walk(folder):
        for file in files:
            # Read file
            srcpath = os.path.join(subdir, file)
            dstpath_in_zip = os.path.relpath(srcpath, start=folder)
            with open(srcpath, 'rb') as infile:
                # Write to zip


Posted by Uli Köhler in Python

How to generate random hex string using OpenSSL

The following command will generate a 32-bytes long random hex string

openssl rand -hex 16


$ openssl rand -hex 16

The command lists 16, not 32 because OpenSSL will generate 16 random binary bytes and then convert these 16 bytes into 32 hex characters.

Posted by Uli Köhler in Linux

How I create my wildcard certificates using certbot

When I’m not using traefik to automagically manage my Let’s Encrypt certificates, this is my preferred way to create Let’s Encrypt wildcard certificates. I use the certbot cloudflare plugin in order to perform the DNS-01 challenge which is required in order to obtain the certificate.

First, I create the script in /etc/letsencrypt/

sudo certbot certonly -d,* --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare-mydomain.ini --preferred-challenges dns-01

and, if not already present, I also create the credentials file /etc/letsencrypt/cloudflare-mydomain.ini (replace the credentials by your cloudflare email and API key):

dns_cloudflare_email = "[email protected]"
dns_cloudflare_api_key = "f4a800573c41858be35aaead4e73d741"

After that, you only have to run the script once:

bash /etc/letsencrypt/

If the process succeeds, your certificate will be in /etc/letsencrypt/live/ However you need to ensure to regularly run certbot renew in order to renew the certificates which are only valid for 3 months.

In our previous post How to automatically renew Let’s Encrypt certbot certs on Ubuntu we provide a simple solution to install a system service to automatically renew the certificates daily. It’s extremely easy to install using just a single command to install the systemd service.

Posted by Uli Köhler in Networking

How to prevent redmine always logging out

In Redmine, by default you are always logged out after a very short period of time.

You can fix this by logging in as an administrator and opening Administration -> Settings -> Authentication and setting Autologin to 365 days. You can also choose a lower value if you want sessions to automatically expire sooner.

Posted by Uli Köhler in Allgemein

How to fix MikroTik RouterOS v7 SSH port forwarding not working: Connection refused

In multiple models of MikroTik routers running RouterOS v7.5 I got Error: Connection refused when running simple SSH port forwarding commands such as

ssh [email protected] -L 8080:

but in the RouterLS log I could see multiple local forwarding forbidden error messages:


In this case, the solution was simple: Go to IP -> SSH in WebFig or Winbox and set Forwarding Enabled to Both and click Apply.

If you have already set Forwarding Enabled to Both or Localon one of my routers this setting wasn’t properly activated – possibly after a RoutreOS upgrade. In order to fix this, set Forwarding Enabled to no, click Apply, then  set Forwarding Enabled to Both and click Apply again. For me, this fixed the issue of being unable to do SSH port forwarding.

Posted by Uli Köhler in MikroTik, Networking

How to print fingerprints of all SSH host keys

In oder to print new-style (SHA256) fingerprints such as Is7oLjYcKue/zRjPKy9gQma/9F24KGaMPHbGberPGAw, use

for i in /etc/ssh/ssh_host_*_key; do sudo ssh-keygen -l -E sha256 -f "$i"; done

Example output:

1024 SHA256:SAhM78NWwqoYS/nRByGs1oqJxrX8fZL/PR/F5oX3QWs [email protected] (DSA)
256 SHA256:V/+9872Yg1i2Q4i7Nt4ef5R06lAuzT6qKemaQEt8EJQ [email protected] (ECDSA)
256 SHA256:Is7oLjYcKue/zRjPKy9gQma/9F24KGaMPHbGberPGAw [email protected] (ED25519)
2048 SHA256:AORSBitRPpwXrbTQzP0Mb/OBXyK+V6gyWf0S4bIh7Ys [email protected] (RSA)

In order to print old-style (MD5) fingerprints such as 5c:19:8b:94:44:5e:b6:15:e6:32:cc:3c:9b:38:6b:4c, use this command:

for i in /etc/ssh/ssh_host_*_key; do sudo ssh-keygen -l -E md5 -f "$i"; done

Example output:

1024 MD5:6d:f8:17:9b:4a:f5:ec:14:04:68:22:9c:ca:2c:a1:19 [email protected] (DSA)
256 MD5:5c:19:8b:94:44:5e:b6:15:e6:32:cc:3c:9b:38:6b:4c [email protected] (ECDSA)
256 MD5:45:dd:7f:91:89:67:ab:2d:ee:a2:f8:9e:18:68:5b:0d [email protected] (ED25519)
2048 MD5:1b:b2:54:3a:10:51:17:b4:49:af:73:c2:da:d6:fc:df [email protected] (RSA)
Posted by Uli Köhler in Linux

TechOverflow: 2000 posts and counting

Not even two years after the 1000th blogpost, this is TechOverflow’s 2001th blog post – and certainly not the last one.

It took 8 years to reach 1000 posts and 7500 visitors a day – and not even two more years to reach up to 13200 visitors in one day (typical work days have around 12.5k visitors), with an upwards trend:

Posted by Uli Köhler in TechOverflow
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Cookie settingsACCEPTPrivacy & Cookies Policy