01/02/2026

Backrest notifications via Telegram

I love restic, I use it on every my server and since the developers added the backup compression feature (previously it has only deduplication) I think it’s the best backup software exisisting.

Using restic on GNU/Linux is a piece of cake and the peace of mind, but on Windows it’s a total different story…

The thing the bothers me the most on Windows is the awful Windows task scheduler and the total absence of notifications (who the hell regularly take a look to the Windows Event Viewer?).

Fortunately there’s a solution for those problem, which is Backrest, a nice web frontend to the restic that brilliantly solves both problems.

In this post I would like to show a nice solution for backup notification using Backrest and Telegram, in this way you’ll get a Telegram notification using Backrest Hooks every time a backup is successful, returns a warning or an error.

First of all open Telegram and find a user called @BotFather

Start a chat with /start and create a new bot with the command /newbot

Give a name and a username to yourbot (for example Backrest Buddy and backrest_buddy)

BotFather will give you a reply like this, the most important information is the API token

Use this token to access the HTTP API:
1234235:069:AAHgPEM2HJIh2Ci1G1l345u2QbbCs876CA
Keep your token secure and store it safely, it can be used by anyone to control your bot.

Open a browser and go to this url
https://api.telegram.org/bot<API TOKEN>/getUpdates

Then open Telegram, open a chat with your bot and write something to it, then get back to the browser and refresh the page, you’ll see some json output with a chat section with a chat id, copy that data, it’s the most important data with the API token.

Now create a powershell script (for example c:\Users\tas\backrest-buddy.ps1) with this syntax, just be careful to insert the API token and the chat id the right spots.

param(
[int]$ExitCode = 0
)

$BotToken = "<API TOKEN>"
$ChatId = "<CHAT ID>"

$HostName = $env:COMPUTERNAME
$Date = Get-Date -Format "dd-MM-yyyy HH:mm:ss"

if ($ExitCode -eq 0) {
$Text = "Backup OK`n------------------------------`n`nHost: $HostName`nDate: $Date"
} else {
$Text = "Backup FAILED`n------------------------------`n`nHost: $HostName`nData: $Date`nExit code: $ExitCode"
}

$Uri = "https://api.telegram.org/bot$BotToken/sendMessage"

$Body = @{
chat_id = $ChatId
text = $Text
parse_mode = "Markdown"
}

Invoke-RestMethod -Uri $Uri -Method Post -Body $Body

Now you can test the notification using this command for successfull backup (change the Powershell script path accordingly to your script)

powershell.exe -NoProfile -ExecutionPolicy Bypass -File c:\Users\tas\backrest-buddy.ps1 0

or this command for a failed backup

powershell.exe -NoProfile -ExecutionPolicy Bypass -File c:\Users\tas\backrest-buddy.ps1 1

If you receive Telegram messages from your bot for a successfull or failed backup everything works as expected.

Now one last step, let’s make Backrest use these powershell script to send us notifications.

Open your Backrest plan settings and go to the Hooks section.
Create three Hooks commands:
– on the first Hook choose CONDITION_SNAPSHOT_SUCCESS as condition
– on the secondo Hook choose CONDITION_SNAPSHOT_WARNING as condition
– on the first Hook choose CONDITION_SNAPSHOT_ERROR as condition

Now insert the same commands you used to test the Telegram notification as Hook commands:
– on the first Hook insert “powershell.exe -NoProfile -ExecutionPolicy Bypass -File c:\Users\tas\backrest-buddy.ps1 0”
– on the second Hook insert “powershell.exe -NoProfile -ExecutionPolicy Bypass -File c:\Users\tas\backrest-buddy.ps1 1”
– on the third Hook insert “powershell.exe -NoProfile -ExecutionPolicy Bypass -File c:\Users\tas\backrest-buddy.ps1 1”

Insert ON_ERROR_FATAL as Error Behavior on all the three hooks.

Ok you did it, now you only have to launch a backup and check your Telegram notifications, if you want to test a failed backup simply add a non existent path to the backup and launch it.

15/01/2026

Amazon Linux 2003 is the devil and you should not use it

I always been a RedHat boy, the first GNU/Linux distro I seriously used was a RedHat Linux 5.0 (please note I’m not talking about RedHat Enteprise Linux, I’m talking about the old RedHat Linux distribution, before RHEL was born) and since then I always tried to stick to the RedHat side of the Linux world, even now If I have to choose a distro I’ll choose Rocky Linux over Debian or Ubuntu.

When I started working on AWS many years ago I tried Amazon Linux 2, and It was good, more or less It was like CentOS 7 and everything was ok… then came Amazon Linux 2003.

I didn’t chose It, someone else did and passed the instance to me, and since the beginning something was not right…

You can’t use EPEL…

You can’t find a lot of RHEL/CentOS/Fedora/Rocky/Alma packages on it…

You can’t even run dnf-automatic or yum-cron to automatically install updates on a scheduled base… updates are shipped in batches and you have to update the whole OS with all the updated packages… manually.

And today I found that even the damn flippin’ cron do not work, you have to manually install it with

sudo yum install cronie -y
sudo systemctl enable crond.service

What on earth?!?!?!?!

No, seriously if you have to choose a RHEL based GNU/Linux distributio choose Fedora, CentOS Stream, Rocky Linux, Alma Linux, Oracle Linux but DO NOT CHOOSE AMAZON LINUX 2003…

Amazon Linux 2003 is the devil of GNU/Linux distros, probably one of the worst distros ever made.

13/10/2025

Update Windows applications with winget

Here’s a quick tip to update your Windows applications using Winget.

If you don’t know this tool winget is the Windows Package Manager available since Windows 10 1809 (build 17763). It works like charm and can get your life waaaaaaayyy easier to keep your system updated.

Basically to update all your Windows applications available through winget you have to open a command prompt as Administrator and launch

winget upgrade --all --silent --accept-source-agreements --accept-package-agreements

If you want to keep some of your applications pinned to a specific version and avoid to update them use

winget pin add <Vendor.Packagename> --blocking

To see the list of pinned packages use

winget pin list

To see the list of your packages use

winget list

23/11/2024

Centos 8 multiple httpd instances

This is an old post I had in draft since… I don’t know, maybe years.

Anyway, CentOS 8 is old and out of support, but still I think running several instances of Apache httpd server through systemd can be useful today in other modern Linux distributions, so I think it’s time to clean up the drafts and publish it.

—————————————————

Recently a customer asked me to setup a webdav access to a vm to change some files inside a couple of java web applications deployed on a Tomcat instance.
My first choice was to configure the webdav servlet already available with Tomcat, which sounds like a nice and elegant solution, but that went wrong because webdav http methods were blocked by some Spring waf protection, and change the java applications for that was not an option (for various reasons I will not explain but not technical ones).

At this point I thought to create a new virtualhost for the webdav access, but in that case file ownership would be a problem, and running the frontend webserver with write permission on all the applications resouces was not a good idea.
The solution was simple, setup a new webserver running as tomcat user (Tomcat file owner) on a different port (and also only available on lan) but I found no documentation with some of the latest distros using systemd.
Yes I know, I could install nginx or another webserver, but running a new Apache configuration felt much more elegant to me.

First of all, create a new httpd systemd unit copying the old one with a new name

cp -v /usr/lib/systemd/system/httpd.service /usr/lib/systemd/system/httpd-dav.service

Copy the main httpd config file for the new webserver

cp -v /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd-dav.conf

Enable the new systemd unit to start at boot

systemctl enable httpd-dav.service

To check if it’s ok run this

systemctl list-unit-files | grep httpd-dav

Now you must edit the new systemd unit to use the new httpd-dav.conf file

systemctl edit httpd-dav.service

…and add this

[Service]
Environment=OPTIONS="-f /etc/httpd/conf/httpd-dav.conf"

Now you must edit the new httpd-dav.conf changing some basic directives to not overlap the main Apache configuration:
In my case I changed Listen PidFile, User, Group, ErrorLog, CustomLog, I removed “IncludeOptional conf.d/*.conf” and added a new virtualhost with mod_dav active, basic authentication, etc etc…
Adjust your Apache configurations as you need, but at least you have to change the Listen and PidFile directives to avoid conflicts with the other httpd process.

When you’re ready you only have to start it with

systemctd start httpd-dav.service

I hope this can be helpful.

27/10/2024

Change Bookstack url and context

I love Bookstack, actually I think it’s one of the best wiki project existing.

It’s well documented, it works like charm, the developer is very active (and he’s also a very kind person, which has nothing to do with the software, but it’s always a pleasure interact with him) and it has very nice features:

  • a nice and responsive design
  • drafts autosave
  • MFA out of the box
  • diagrams.net integration

It also works perfectly fine in a docker container, technically the official project do not offer a container image, but there are two groups building them and they’re referred directly in the official documentation.

Recently I started to sort things out on my beloved Raspberry PI 5, in particular I’m moving services so I can reverse proxy them on a single Apache httpd instance (you know I still love Apache :D ), today I moved around Bookstack, in particulare I did two things:

  1. change Bookstack hostname (for example from https://site.domain.tld to https://newsite.domain.tld )
  2. make Bookstack work under a specific url context (for example https://site.domain.tld/bookstack instead of https://site.domain.tld ).

On my environment I’m using the LinuxServer.io docker image, so check the project site for details, and also I’m using docker compose, if you’re not familiar with it start using it for Reorx’s sake.

Backup

First of all take a damn backup, it’s mandatory.

Seriously I’m not joking.

Stop the containers

cd /data/docker/bookstack ; docker compose down

Backup files with a simple tar, restic, kopia, whatever you want, but DO IT!

cd /data/docker/ ; tar -cpzf /backup/bookstack-backup.tar.gz bookstack

Change Bookstack hostname

This process is documented on the Bookstack documentation (LINK), but still I decided to mention it because the procedure is a little bit different on a docker container, so it’s worth spent a few words about it.

First of all you have to change the APP_URL configuration variable, in case of a docker container it’s enough to change the environment variable on the docker-compose.yaml file, so open the file and change the variable to the new url

Now you must replace the old url from the database record with the new one using the bookstack:update-url command, in case of a docker container you must identify where’s the Laravel framework artisan file and launch it accordingly to the documentation.

docker exec -it bookstack php /app/www/artisan bookstack:update-url https://site.domain.tld https://newsite.domain.tld

After that clear the cache using

docker exec -it bookstack php /app/www/artisan cache:clear

Restart the docker container to change the environment variable you previously changed with the new url.

cd /data/docker/bookstack ; docker compose down ; docker compose up -d

Done, now your Bookstack instance should be reachable to the new url.

 

Change Bookstack root context

This change is a little bit tricky, because it involves some webserver changes.

First of all you must repeat the same process used for changing the url hostname of your Bookstack instance, this time including the context you want to use (for example /bookstack ).

Let’s quickly review the steps:

1) Change the APP_URL environment variable in the docker-compose.yaml (APP_URL=https://newsite.domain.tld/bookstack in this case)

2) Replace the url in the database using the bookstack:update-url

docker exec -it bookstack php /app/www/artisan bookstack:update-url https://newsite.domain.tld https://newsite.domain.tld/bookstack

3) Clear Bookstack cache

docker exec -it bookstack php /app/www/artisan cache:clear

4) Restart the docker container to change the environment variable you previously changed with the new url.

cd /data/docker/bookstack ; docker compose down ; docker compose up -d

Now you must review the webserver configuration inside your docker container, in case of the LinuxServer.io container there’s a nginx instance running inside the container, you can fine its configuration inside the /config/nginx/ directory inside the container.

If you followed the LinuxServer.io recommendations the /config directory should be a persistent volume (or a persistent path on your docker host), so any changes in the nginx configuration files should not be lost in case of a container restart.

In my case the config persistent volume is located in the /data/docker/bookstack/bookstack-config directory, so the nginx configuration is located in the file /data/docker/bookstack/bookstack-config/nginx/site-confs/default.conf.

Apply this patch

wget https://tasslehoff.burrfoot.it/pub/bookstack-nginx.patch ; \
patch /data/docker/bookstack/bookstack-config/nginx/site-confs/default.conf < bookstack-nginx.patch

Reload nginx configuration

docker exec -it bookstack nginx -s reload

Done, now your Bookstack instance should work at the new url https://newsite.domain.tld/bookstack

« Post precedenti