Herkos

Your own Blossom server

Last updated: 27 October 2026

Herkos Drive cuts each file into encrypted blocks and keeps them on several Blossom servers: three copies, on at least two kinds of server, and one on a server of yours. Public servers keep blocks for free, but they promise nothing: they can purge them, fill up or close.

A Blossom server of your own keeps your copy for as long as you decide and takes blocks only from your Drive. This page explains how to set one up in three steps, plus a nightly backup. It goes well with your own mailbox relay, and it takes about fifteen minutes if you already have a server.

Which server to use

blossom-server by hzrd149, version 6.4.1 (6 October 2026, MIT licence). We ran it on 27 October 2026 with the configuration below and Drive's own upload code, and checked what Drive needs, not what the server advertises:

What Drive does Result
Uploads 8 MiB encrypted blocks (application/octet-stream) Accepted
Asks before uploading (BUD-06) Answers, and turns away any other key
Asks your server to copy a block from a public one (BUD-04) Copied
Reads a block without identifying Served, byte for byte
Deletes a block with the key that uploaded it Deleted: asking again gives 404
Uploads from any other key Refused (401)

What you need

Step 1: run the server

Make a folder for it, for example /root/blossom, and save these two files in it. The first is docker-compose.yml:

services:
  blossom:
    image: ghcr.io/hzrd149/blossom-server:6.4.1
    environment:
      BLOSSOM_REQUIRE_CONFIG: "1"
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - data:/app/data
      - ./config.yml:/app/config.yml:ro
    restart: unless-stopped
volumes:
  data: {}

The second is config.yml. Put your domain in publicDomain and your volume key where it says YOUR-VOLUME-KEY:

port: 3000
host: 0.0.0.0
publicDomain: blossom.example.com
storage:
  backend: local
  local:
    dir: ./data/blobs
  rules:
    - type: "*"
      expiration: 100 years
      pubkeys:
        - "YOUR-VOLUME-KEY"
upload:
  requireAuth: true
  maxSize: 104857600
  requirePubkeyInRule: true
mirror:
  enabled: true
  requireAuth: true
delete:
  requireAuth: true
list:
  enabled: false
media:
  enabled: false
report:
  enabled: false
landing:
  enabled: false

What it says: only your volume key may upload or ask for copies (requirePubkeyInRule), and a block is kept until Drive deletes it. The server needs an expiry in every rule; 100 years without being read is never, in practice, and Drive reads its blocks regularly anyway. The port only listens on the machine itself (127.0.0.1): everything goes through the proxy. Start it:

cd /root/blossom
docker compose up -d

Step 2: put HTTPS in front

With Caddy, the whole configuration (/etc/caddy/Caddyfile) is:

blossom.example.com {
    reverse_proxy 127.0.0.1:3000
}

If the same Caddy already serves your relay, add this block below that one. Reload Caddy (sudo systemctl reload caddy). Opening https://blossom.example.com in a browser should now answer, with a 404: there is no home page, on purpose.

Step 3: tell Herkos Drive

  1. In Herkos Drive, open Settings → Servers, add https://blossom.example.com and mark it as yours.
  2. Drive asks it before each upload. From then on, every new file gets a copy there, and the files you already had get theirs the next time Drive checks the copies.
  3. Keep the other servers in the list. Yours is one of the three copies, never the only one: if it goes down, your files are still on the others, and Drive tells you after a week of silence.

Step 4: back it up every night

If your server dies, Drive notices and gives the affected files copies on the other servers in your list. But the copy on your server is the only one that depends on nobody else, and a nightly backup lets you rebuild it without copying everything again over the internet.

The server keeps everything inside the Docker volume blossom_data (the name starts with the folder the files are in; check it with docker volume ls): the blocks, one file each, and a small SQLite database that says who uploaded each one. Blocks never change, but the database must not be copied while it is being written. So the script copies everything with the server running, stops it for the few seconds it takes to copy what changed in between, starts it again, and only then uploads the copy. While it is stopped, Drive simply tries again a little later.

The copy goes off the server with restic, which encrypts it before it leaves. Use a place at a different provider from the server: an SFTP account (a storage box, a computer at home) or an S3-compatible bucket. If you already back up your relay this way, use the same place with another folder.

  1. Install the tools and create the repository. Keep the password in your password manager as well: without it the copies cannot be read, and if it only lives on the server, it dies with the server. For SFTP, root also needs an SSH key that the backup machine accepts (sudo ssh-keygen -t ed25519, then add /root/.ssh/id_ed25519.pub there).

    sudo apt install restic rsync
    sudo sh -c 'openssl rand -base64 32 > /root/.restic-blossom-password'
    sudo sh -c 'cat > /root/.restic-blossom-env' <<'END'
    export RESTIC_REPOSITORY=sftp:backup@backup.example.com:/blossom
    export RESTIC_PASSWORD_FILE=/root/.restic-blossom-password
    END
    sudo chmod 600 /root/.restic-blossom-password /root/.restic-blossom-env
    sudo sh -c '. /root/.restic-blossom-env && restic init'
    
  2. Save this as /usr/local/bin/backup-blossom and make it executable (sudo chmod 700 /usr/local/bin/backup-blossom). Change BLOSSOM_DIR if your files are not in /root/blossom. It keeps the last 14 days, 8 weeks and 12 months. Blocks never change, so each night only adds what is new.

    #!/bin/sh
    set -eu
    . /root/.restic-blossom-env
    BLOSSOM_DIR=/root/blossom
    COPY=/var/backups/blossom-data
    VOLUME=$(docker volume inspect --format '{{ .Mountpoint }}' blossom_data)
    mkdir -p "$COPY"
    rsync -a --delete "$VOLUME/" "$COPY/"
    cd "$BLOSSOM_DIR"
    docker compose stop blossom
    trap 'docker compose start blossom' EXIT
    rsync -a --delete "$VOLUME/" "$COPY/"
    docker compose start blossom
    trap - EXIT
    restic backup "$COPY" --tag blossom
    restic forget --tag blossom --keep-daily 14 --keep-weekly 8 --keep-monthly 12 --prune
    
  3. Run it once by hand (sudo /usr/local/bin/backup-blossom), then schedule it with sudo crontab -e. The second line reads back a tenth of the stored data each month, a different tenth every time, to prove the copies can still be opened. Look at /var/log/backup-blossom.log now and then: a line with Fatal means that night's copy failed. If the relay is backed up on the same machine, these times do not overlap with its own.

    35 4 * * * /usr/local/bin/backup-blossom >> /var/log/backup-blossom.log 2>&1
    55 5 2 * * . /root/.restic-blossom-env && restic check --read-data-subset=10% >> /var/log/backup-blossom.log 2>&1
    

To restore (a new server, or after a disk failure): set the server up again (steps 1 and 2) and repeat points 1 and 2 of this step with the same repository, but write the password you saved into /root/.restic-blossom-password instead of creating a new one, and skip restic init. Then copy the latest backup into the server's volume:

sudo sh -c '. /root/.restic-blossom-env && restic restore latest --tag blossom --target /'
cd /root/blossom && sudo docker compose stop blossom
sudo rsync -a --delete /var/backups/blossom-data/ "$(sudo docker volume inspect --format '{{ .Mountpoint }}' blossom_data)/"
sudo docker compose start blossom

The next time it checks the copies, Drive finds the blocks back and copies over, from the other servers, whatever arrived after the last backup. We tried this whole chain on 27 October 2026: backup, server deleted, restore, every block read back with its own hash, and deleted afterwards with Drive's key.

Once a year, do this restore on a spare machine and check that the server answers with your blocks: it is the only test that proves the whole chain, password included.

Good to know