Skip to content

2025

move-docker-root

Moving a docker root is not hard, but comes with 1 or 2 gotchas. In my case, I wanted to attach a disk to the docker root placed in /srv/docker, so I had to move the data out of the way, mount the disk, and than move it back.

Gotchas: - Just running systemctl stop docker won’t actually stop docker - File permissions for overlay2 is VERY important (you will run into permission denied on /tmp or similar) - Finding the right commands :)

How To:

# Getting Docker to shut up
systemctl disable --now docker.socket
systemctl disable --now docker.service

# Getting the data out of the way
mv /srv/docker /root/docker

# Now do what you must
# if need be, stop it again and rm -rf /srv/docker/*

# Lets get the data back
cp -R -a /root/docker/* /srv/docker/

# Start docker and check that all is good
systemctl enable --now docker.service
systemctl enable --now docker.socket

# Now the fun part, somehow overlay2 sometimes mounts the folder from your moved files. fun right? just umount those
umount /root/docker/overlay2/*/merged

# If so, cleanup after yourself
rm -rf /root/docker

Fix USB Modem

We have a USB stick with a SIM in it, for sending SMS. When pluging in the new stick, it always was showing up as a CD ROM instead of a usb-serial. This is what we did to solve it:

dnf install -y usbutils usb_modeswitch python3-pip git

# Setup USB Modem
lsusb
# > Bus 001 Device 004: ID 3566:2001 Mobile Mobile

echo "#!/bin/bash

# Fix USB Mode
/usr/sbin/usb_modeswitch -v 0x3566 -p 0x2001 -X

# Wait 3 seconds than read that stick for usb serials
sleep 3
echo '3566 2001 ff' | sudo tee /sys/bus/usb-serial/drivers/option1/new_id" > /usr/local/sbin/fix-huawei-stick.sh
chmod +x /usr/local/sbin/fix-huawei-stick.sh

echo 'ATTRS{idVendor}=="3566", ATTRS{idProduct}=="2001", RUN+="/usr/local/sbin/fix-huawei-stick.sh"' > /etc/udev/rules.d/99-huawei.rules

udevadm control --reload-rules
udevadm trigger


echo "usbserial
option" > /etc/modules-load.d/sms.conf


reboot

silverbullet-decimal-index

I like the Johnny.Decimal System, so I try to use it in Silverbullet. To make navigation easier, I’ve created a widget, that shows me all matching notes under the current one.

-- priority: 30
event.listen {
  name = "hooks:renderBottomWidgets",
  run = function(e)
    local currentPage = editor.getCurrentPage():match("[^/]+$")

    -- check last path segment, that it is decimal system
    local isMatch = currentPage:match("^[A-Z] .*$") or currentPage:match("^[A-Z][0-9][0-9] .*$") or currentPage:match("^[0-9][0-9] .*$") or currentPage:match("^[0-9][0-9]\\.[0-9][0-9] .*$")

    if isMatch then

      local subPages = query[[
        from p = index.tag "page"
        where p.name:match("^"..editor.getCurrentPage().."/[^/]+$")
        select {
          Index = "[[" .. p.name .. "]]",
        }
      ]]
      print("index subpages", #subPages)
      if #subPages > 0 then
        return widget.new {markdown = "![[meta/templates/decimal-index]]"}
      end
    end

    return nil

  end
}

silverbullet-files-handling

I love my Silverbullet notes instance. But the file handling was not perfect for me. So I created a small setup, that will help me with handling the files, and mapping them to pages:

Uploads

I’ve created a upload button in my action bar, that handles where to put the file. It just keeps the original file name. But this in your config:

config.set {
  actionButtons = {
    {
      icon = "upload",
      run = function()
        local file = editor.uploadFile("", nil)
        local newFileName = editor.getCurrentPage().."_files/".. file.name
        space.writeDocument(newFileName, file.content)
        editor.insertAtCursor("[["..newFileName.."]]")
      end,
      description = "Add a new attachement"
    }
 }
}

Showing the Files

To see which files belong to a site, I have this widget added under meta/files-widget, but you can put it where ever you like:

-- priority: 20
event.listen {
  name = "hooks:renderBottomWidgets",
  run = function(e)
    -- Get Files
    local files = space.listFiles()

    -- Loop
    local hasFiles = false
    local filesOut = "| File | Size | Changed |\n"
    filesOut = filesOut .. "|---|---|---|\n"
    for _, file in ipairs(files) do
        -- If lies under the current page
        if(starts_with(file.name, editor.getCurrentPage().. "_files/")) then
          -- not .md files
          if(not ends_with(file.name, ".md")) then

            filesOut = filesOut .. "|[["..file.name.."]]"
            filesOut = filesOut .. "|" .. human_readable_bytes(file.size)
            filesOut = filesOut .."|"..os.date("%Y-%m-%d %H:%M:%S", file.lastModified / 1000)
            filesOut = filesOut .."|\n"
            hasFiles = true
          end
        end
    end
    if hasFiles then
      return widget.new {markdown = filesOut}
    else
      return nil
    end
  end
}

Silverbullet to GitHub

This website is hosted on GitHub Pages, and the content is directly synced from my Silverbullet instance, to GitHub. I’ve set it up this way:

Setup

To set this up, you’ll have to create a ssh key-pair, and put it into your space somewhere. I’ve put it into a folder Z Public/.keys inside my instance. Then add that public key to your GitHub account, or to your repository, so it can be used to sync the content.

I also added a Z Public/.gitignore to exclude the key, like this:

.keys/*

Then set this folder up as a new git repo, and add the origin to it.

Now you need to create a github workflow and a config to make it work with mkdocs. Both can be found in the GitHub Repo.

Sync Command

After that, create a new page in your Silverbullet (for me it lives in meta/command-git-sync-github) and put this script in it:

command.define{
  name = "Public-Site: Sync",
  run = function()

    local function gitRun(args)
      local ok = shell.run("bash", {"-c" ,args .. " >> /tmp/git-log 2>&1"})
      return ok
    end

    gitRun("GIT_SSH_COMMAND=\"ssh -i '/space/Z Public/.keys/id_rsa' -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git -C '/space/Z Public' pull --no-commit --rebase origin master")

    local ok1 = gitRun("git -C '/space/Z Public' add . --all")
    local ok2 = gitRun("git -C '/space/Z Public' commit -m auto")
    local ok3 = gitRun("GIT_SSH_COMMAND=\"ssh -i '/space/Z Public/.keys/id_rsa' -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git -C '/space/Z Public' push origin master")

    if ok1 and ok2 and ok3 then
      editor.flashNotification("synced")
    else
      editor.flashNotification("sync did not work")
    end
  end
}

mysqldump is locking the db

If your mysqldump command is locking the DB and you can’t edit the command (because another application is starting it), just add this to your /etc/my.cnf:

[mysqldump]
single-transaction
quick
skip-lock-tables

Set ESXi Syslog Hosts with Script

Here's the script:

$CONFIG = @{
    "logServer" = "graylog.domain.local";
    "logPort" = 1514;
    "logProto" = "tcp";
    "vcenter" = "vcsa.domain.local";
    "cluster" = "Cluster1";
}

# Startup
Import-Module VCF.PowerCLI
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-Null
$syslogURL = "$($CONFIG["logProto"])://$($CONFIG["logServer"]):$($CONFIG["logPort"])"

# Connect
Connect-VIServer -Server $CONFIG["vcenter"]

# Loop through hosts
$vmHosts = Get-Cluster -Name $CONFIG["cluster"] | Get-VMHost 
foreach($vmHost in $vmHosts) {
    Write-Host "Configuring $($vmHost.Name)"

    # Set syslog
    $advancedSetting = Get-AdvancedSetting -Entity $vmHost -Name "Syslog.global.logHost"
    $advancedSetting | Set-AdvancedSetting -Value $syslogURL -Confirm:$false -ErrorAction Stop | Out-null

    # Activate Firewall rule
    $rule = Get-VMHostFirewallException -VMHost $vmHost | ? {$_.Name -eq "syslog"}
    if($rule) {
        $rule | Set-VMHostFirewallException -Enabled:$true -ErrorAction Stop | Out-Null
    } else {
        Write-Host "ERROR - Rule not found"
        exit
    }

    # Reload syslog service
    $esxcli = Get-EsxCli -VMHost $vmHost
    $esxcli.system.syslog.reload.Invoke() | out-null
}

Disconnect-VIServer -Server $CONFIG["vcenter"] -Confirm:$false

Yourls – Generate Password

php -r "echo 'phppass:'. password_hash('yourpassword', PASSWORD_BCRYPT) . PHP_EOL;"

GeoIP in Nginx Proxy Manager

I’ve tried to add GeoIP2 to my Nginx Proxy Manager. After finding a few blogs and articles that “explain” how to do it, I got more confused. Here is the final guide, that will help you.

First you need to setup a 2nd docker container, that will download your GeoIP2 database from maxmind. They got a docker image for that. Should be simple.

After that, add the databases to your nginx proxymanager like this:

(...)
services:
  npm:
    (...)
    volumes:
      (...)
      - /your/geoip/folder:/geoip

Now add files to the nginx proxy manager data volume.

Add/create the file nginx/custom/http_top.conf to your npm data folder:

geoip2 /geoip/GeoLite2-Country.mmdb {
    $geoip2_data_country_iso_code country iso_code;
}


map $geoip2_data_country_iso_code $allowed_country {
  default 0;
  CH 1;
  # Add your countries like CH here
}

Add/create the file nginx/custom/root_top.conf to your npm data folder:

load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so;
load_module /usr/lib/nginx/modules/ngx_stream_geoip2_module.so;

Now add this to all your proxies (or the ones you want it to work for) in the advanced tab:

if ($allowed_country = 0) {
    return 404;
}

IT WORKS NOW!

Opensearch – Yellow / Top Queries

With an update of opensearch there suddenly is a new index called top_queries-xxxx. This will turn yellow on a single-node cluster, as it want’s two replicase. Apperantely, it’s not in the interest of opensearch, to create stuff, that works with their own software. So we have to fix it ourtselfs. Once again.

Luckely this is easy to do:

/usr/share/opensearch/bin/opensearch-plugin remove query-insights

This will delete the plugin that creates these indices. Now just restart the service, delete the current one and you’re all good:

# Restart Opensearch
systemctl restart opensearch

# Check if only the to be deleted indices are shown here
curl -XGET localhost:9200/_cat/indices | grep "top_queries-2" | awk '{print $3}'

# If yes, continue
curl -XGET localhost:9200/_cat/indices | grep "top_queries-2" | awk '{print $3}' | xargs -I {} curl -X DELETE localhost:9200/{}