start script with systemd

I wanted to shoot as many photos as possible with a raspicam. But no photos at night (the hours are in UTC). Save the file as /root/ and set the file as executable.


mkdir /root/images

while true
  sleep 1
  if (( `date +%H` < 21 )); then
    if (( `date +%H` > 2 )); then
      /opt/vc/bin/raspistill -o /root/images/`date +%s`.jpg -rot 180

Archlinuxarm doesn't have a rc.local file. So I had to use systemd to start the script and keep it alive:

Description=Camera shooting service



Save as /etc/systemd/system/camera.service and enable it with systemctl enable camera.service. It will start now at the next boot.

Find active servers in subnet

I needed an org-mode table with a list of all currently used IP addresses in a subnet.

#+BEGIN_SRC bash
  nmap -sP | grep "scan report" | cut -d" " -f5 | sort -V

| |
| |
| |
The nmap command returns for each server 2 lines.
The line with the IP address contains "scan report".
cut splits the IP address from the line and sort -V sorts by IP address.

To run a command in org-mode you press C-c C-c inside the region and answer the security question with y.

Scaleway serverless

Disclaimer: This walkthrough worked in the moment of the writing of this post and may be broken in the future.

I was very happy when Scaleway announced their "function as a service"-service. There is a need for more competition to AWS, Google or Azure.

To get a feeling for the complexity I want to describe how I deployed a function at scaleway. Their api documentation is better than I expected but the walkthrough is using serverless and nodejs.

I wanted to try this with Python 3, PyTorch and curl only. I failed with PyTorch because of an issue with a shared library in Alpine Linux.

So this walkthrough only uses numpy as an example. But this should work for all Python libraries that can run with Alpine Linux.

First you need your organization_id and a new token from your scaleway settings. This is described in the documentation.

Then you need a namespace:

curl -X POST "" \
-H "accept: application/json" -H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
-d "{\"name\": \"hello-ns\", \"organization_id\": \"$ORGANIZATION_ID\"}"

To be sure everything worked, we list the namespaces:

curl "" -H "accept: application/json" \
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json"

Now the interesting part. We need the namespace_id created before. Create a function:

curl -X POST -H "X-Auth-Token: $TOKEN" \
"" \
-d "{\"name\": \"hello\", \"namespace_id\": \"$NAMESPACE_ID\", \"memory_limit\": 128, \"min_scale\": 0, \"max_scale\": 1, \"runtime\": \"python3\"}"

In the response is an id we need next. For uploading the function we need to create a zip file with the name like this: function-$

The files we want to add to our zipfile are


import numpy

def handle(event, context):
    return {
        "body": {
            "numpy": {
              "version": numpy.__version__,
        "statusCode": 200,
  • packages

mkdir package
# install packages using the alpine image from scaleway for this
docker run --rm -v $(pwd):/home/app/function --workdir /home/app/function pip install numpy --target ./package

Generate zip and get size of zip:

# generate zip
zip -r function-$ package
# get size of zip for upload calls
ls -l *zip

For my test the size of the zip file was 12003113 bytes.

get upload url (insert TOKEN, FUNCTION_ID and SIZE_OF_ZIP in bytes)

curl -X GET -H "X-Auth-Token: TOKEN"

Upload using the url, zip-filename and zip-size from before:

export FUNCTION_ARCHIVE_URL=$(echo -n "<upload-url-from-before>")
curl -H "Content-Type: application/octet-stream" -H "Content-Length: SIZE_OF_ZIP" --upload-file FUNCTION_ARCHIVE_NAME $FUNCTION_ARCHIVE_URL

Now deploy the function from the uploaded storage:

curl -X POST -H "X-Auth-Token: $TOKEN" "$FUNCTION_ID/deploy" -d "{}"

The last call returns the url to invoke in the function. Use this url like this:

curl "<url-given-by-deploy>"

This should result in something like this: