Run it for your lab

One lab, one database.

Set-up recipes for BioManager. Pick a route, do its steps in order, and stop at Done when. Most labs pay nothing.

Part one

Start here

Pick a route

  1. How many people? Just you: A. More than one: question 2.
  2. Do they need it outside the lab network? (At home, on phones on mobile data, at another site. Your campus VPN counts as inside.) Yes: D, or E if some of them can't install the Tailscale app. No: question 3.
  3. Does your IT give you a server? Yes: C. No: B.
RouteWho can reach itCost per monthSet-up time
A · One computerThat computer only$02 minutes
B · Linux computer in the labThe lab or campus network, and its VPN$0 with a spare computer (a new small PC is a one-off $300–600)1 hour
C · University serverThe campus network and VPNOften $0; some IT departments chargeDepends on IT, then 30 minutes
D · Cloud VM + Tailscale recommendedYour lab, from anywhere; no one else$0 (Oracle Cloud Always Free + Tailscale's free plan), or about $5–7 for a paid VM1 hour
E · Cloud VM + your own domainAnyone with the address (they still need an account)As D, plus about $1 for the domain ($10–15 a year)1–2 hours

Prices as charged in September 2026; check before you sign up. Every server route (B–E) includes nightly backups, a weekly restore test and HTTPS.

Or let the desktop app do itIn the desktop app, Settings → Set up a lab server does B, C, D or E for you, and on a Mac sets up that Mac for the whole lab. It shows each step before running it and can bring your records along.

Set it up with an AI assistant

An AI assistant with a terminal (Claude Code, Cursor, Codex and the like) can follow route B, C, D or E for you from deploy-with-ai.md, a version of this page written for it. Paste this to your assistant:

Read https://biomanager.org/deploy-with-ai.md and follow it to set up a BioManager server for my lab. Start by asking me the questions it lists, recommend an option, and wait for my go-ahead before creating anything that costs money or opens the server to the internet. Never ask me to paste passwords or keys into this chat: tell me where to type them myself.

You still create the accounts, approve sign-ins and type every password yourself; the guide makes the assistant stop and ask at each of those.

Part two

Routes

A · One computer: the desktop app

One person$02 minutes

You need

  • A Mac (Apple silicon or Intel), a Windows PC, or a Linux PC (x86-64).
  • Cost: $0 a month.
  • Time: 2 minutes.
  1. Download the file for your computer from the Download page.

    You should see: BioManager-macOS-AppleSilicon.zip, BioManager-macOS-Intel.zip, BioManager-Windows.zip or BioManager-Linux.AppImage in your Downloads folder.

    If not: on a Mac, Apple menu → About This Mac says which chip you have.

  2. Open it.

    Mac: unzip it, drag BioManager into Applications, then right-click it → Open → Open. Windows: extract the zip, open BioManager.exe, and at the warning choose More info → Run anyway. Linux:

    cd ~/Downloads && chmod +x BioManager-Linux.AppImage && ./BioManager-Linux.AppImage

    You should see: the BioManager window, asking you to create an account.

    If not: a Mac that offers only Move to Trash: System Settings → Privacy & Security → Open Anyway. A Linux AppImage that won't start needs FUSE: install libfuse2.

  3. Create your account and answer the set-up questions (user guide).

    You should see: BioManager's Home page, with the databases you ticked.

Done when: BioManager opens on Home, signed in as you.

Never put the data folder in Dropbox, OneDrive, Google Drive or iCloud DriveSyncing corrupts the database. The data lives in ~/Library/Application Support/Biomanager/ (Mac), %APPDATA%\Biomanager\ (Windows) or ~/.local/share/Biomanager/ (Linux): back that folder up with Time Machine or File History.

A few people on one trusted network can share this computer's lab without a server: Settings → Devices → Share this lab on the network. It has no nightly backup and no encryption (Devices).

B · A Linux computer in the lab

The lab network$0 with a spare computer1 hour

You need

  • A Linux computer that stays on (Ubuntu 24.04, or another Linux with systemd), x86-64 or ARM, with 2 CPU cores, 2 GB of memory and 20 GB of free disk. For a Mac, use the desktop app: Settings → Set up a lab server → This computer, for the whole lab.
  • An account on it that can use sudo.
  • A fixed IP address or DNS name for it, from your IT.
  • Cost: $0 a month.
  • Time: about an hour.
  1. Ask IT for a fixed IP address or DNS name for this computer, and turn off sleep (Ubuntu: Settings → Power → Automatic Suspend → Off). Then check its addresses:

    hostname -I

    You should see: the IP address IT gave you, among others.

    If not: the address changes after a restart: ask IT for a DHCP reservation for this computer.

  2. Install Docker.

    curl -fsSL https://get.docker.com | sudo sh
    sudo usermod -aG docker $USER

    You should see: Docker's installer finish without an error.

    If not: sudo: a password is required or not in the sudoers file: use an account that can use sudo, or ask IT.

  3. Log out, log in again, and check Docker.

    docker compose version

    You should see: Docker Compose version v…

    If not: permission denied while trying to connect to the Docker daemon socket: you haven't logged in again since step 2. Log out and in (or restart).

  4. Download BioManager.

    sudo mkdir -p /opt/biomanager && sudo chown "$USER" /opt/biomanager
    curl -fsSL -o /tmp/biomanager-server.tar.gz \
      https://github.com/gaspolymerase/biomanager/releases/latest/download/biomanager-server.tar.gz
    tar -xzf /tmp/biomanager-server.tar.gz -C /opt/biomanager
    /opt/biomanager/Biomanager/deploy/host/load-image.sh

    You should see: Downloading BioManager <version> for amd64… (or arm64), then Loaded image: ghcr.io/gaspolymerase/biomanager:<version> and Loaded. Start or update with: …

    If not: No BioManager image for …: BioManager runs on x86-64 and ARM64 only. Could not download: this computer can't reach github.com; ask IT about a proxy, then run the last line again.

  5. Write the settings that need no choices: a database password, the certificate type and the backup folder.

    cd /opt/biomanager/Biomanager/deploy
    cp .env.example .env && chmod 600 .env
    sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 24)|" .env
    sed -i "s|^TLS=.*|TLS=internal|; s|^BACKUP_DIR=.*|BACKUP_DIR=/opt/biomanager/backups|" .env

    You should see: no output.

    If not: cannot stat '.env.example': step 4 didn't unpack. Run step 4 again.

  6. Enter your address and time zone. Open the settings file:

    nano .env

    Change these two lines to read as below, then save with Ctrl+O, Enter and close with Ctrl+X:

    DOMAIN=<your-domain>
    TZ=<your-time-zone>

    <your-domain>: the fixed IP address or DNS name from step 1, without https://. <your-time-zone>: like Europe/Berlin or Asia/Shanghai; timedatectl shows this computer's. With a second disk, also set BACKUP_DIR to a folder on it.

    You should see: DOMAIN= near the top of the file, TZ= about halfway down.

    If not: nano: command not found: sudo apt-get install nano.

  7. Check the settings.

    grep -E '^(DOMAIN|TLS|TZ|BACKUP_DIR)=' .env
    docker compose config --quiet && echo "settings ok"

    You should see: your four lines, with TLS=internal, then settings ok.

    If not: set POSTGRES_PASSWORD in .env: run step 5 again. DOMAIN=biomanager.example.edu: the edit wasn't saved; repeat step 6.

  8. Start BioManager.

    docker compose up -d --build

    You should see: after a minute or two, biomanager-db-1, biomanager-app-1, biomanager-caddy-1 and biomanager-backup-1, each Started or Healthy.

    If not: pull access denied for ghcr.io/gaspolymerase/biomanager: run host/load-image.sh, then this again. container biomanager-app-1 is unhealthy: docker compose logs --tail 100 app says why.

  9. Check that everything runs.

    docker compose ps

    You should see: db and app (healthy), caddy Up, and backup (healthy), or (health: starting) for a few minutes while it takes its first backup.

    If not: a service missing or Restarting: docker compose logs --tail 100 <service>.

  10. Get the setup code for the first account.

    docker compose logs app | grep "setup code"

    You should see: No accounts yet. Create the first admin at /register with setup code 1a2b-3c4d-5e6f (also saved in /data/setup-code).

    If not: nothing: the app is still starting. Wait a minute and repeat, or run docker compose exec app cat /data/setup-code.

  11. Save BioManager's certificate, for the lab's devices to trust.

    docker compose exec caddy cat /data/caddy/pki/authorities/local/root.crt > biomanager-root.crt
    head -1 biomanager-root.crt

    You should see: -----BEGIN CERTIFICATE-----

    If not: No such file or directory: Caddy is still starting. Wait ten seconds and repeat.

  12. On each computer and phone that will open BioManager, once: copy biomanager-root.crt to it (email, USB stick, AirDrop) and install it.

    • Mac: double-click it; in Keychain Access, open it and set Trust → When using this certificate → Always Trust.
    • Windows: double-click it → Install Certificate → Local Machine → Place all certificates in the following store → Trusted Root Certification Authorities.
    • iPhone, iPad: open it, install the profile in Settings → General → VPN & Device Management, then turn it on in Settings → General → About → Certificate Trust Settings.
    • Android: Settings → Security → Encryption & credentials → Install a certificate → CA certificate.

    You should see: https://<your-domain> open on that device with no certificate warning.

    If not: on an iPhone, the switch in Certificate Trust Settings is still off. Firefox keeps its own list: Settings → Privacy & Security → View Certificates → Authorities → Import.

  13. On one of those devices, open https://<your-domain>/register, fill in your name, a username, a password and the Setup code from step 10, and choose Create account. This first account is the lab's admin.

    You should see: you are signed in, and the lab's set-up questions open.

    If not: That setup code is not right.: copy it again from step 10, all three groups and the dashes.

  14. Turn on alerts, the watchdog and weekly updates.

    sudo host/install.sh

    You should see: Installed. Alerts go to ntfy topic: biomanager-… and two timers, biomanager-watchdog.timer and biomanager-maintenance.timer. Subscribe to that topic in the ntfy app on your phone.

    If not: command not found: you're not in the deploy folder. Run cd /opt/biomanager/Biomanager/deploy first.

Done when: on another computer on the lab network, https://<your-domain>/healthz shows ok with no certificate warning, and you can sign in at https://<your-domain> as the admin.

One computer holds the lab and its backupsIf its disk dies, both are gone. Put BACKUP_DIR on a second disk and turn on off-site copies (After it's running).

C · A university or department server

Campus network and VPNOften $0IT, then 30 minutes

You need

  • A Linux VM from IT, with an account on it that can use sudo (step 1 asks for both).
  • A certificate from IT for its name: a certificate file and its key file.
  • Your computer, on the campus network or VPN, with a terminal.
  • Cost: often $0 a month; some IT departments charge.
  • Time: IT's turnaround, then 30 minutes.
  1. Send IT this request.

    We'd like a small Linux VM (Ubuntu 24.04, 2 vCPU, 4 GB RAM, 40 GB disk) with Docker and its compose plugin, a DNS name such as biomanager.ourdept.example.edu, a TLS certificate for that name, and port 443 open to the campus network and VPN. It runs BioManager, a lab database, in Docker containers. Please give me an account on it that can use sudo.

    You should see: a reply with the DNS name, how to sign in, and the certificate: a .crt or .pem file and its key.

    If not: IT can't issue a certificate: set TLS=internal in step 6 instead of files, skip step 5, and do route B's steps 11 and 12 after starting.

  2. Copy IT's two certificate files to the VM, from your computer.

    scp <certificate-file> <key-file> <you>@<your-domain>:

    <certificate-file>, <key-file>: the files from IT (in step 5, just their names). <you>: your account on the VM. <your-domain>: the VM's DNS name.

    You should see: both files copied, each at 100%.

    If not: Connection timed out: you're off campus. Connect the VPN.

  3. Sign in to the VM and check Docker.

    ssh <you>@<your-domain>
    docker compose version

    You should see: Docker Compose version v…

    If not: permission denied while trying to connect to the Docker daemon socket: run sudo usermod -aG docker $USER, sign out and in again. docker: command not found: ask IT to install Docker.

  4. Download BioManager.

    sudo mkdir -p /opt/biomanager && sudo chown "$USER" /opt/biomanager
    curl -fsSL -o /tmp/biomanager-server.tar.gz \
      https://github.com/gaspolymerase/biomanager/releases/latest/download/biomanager-server.tar.gz
    tar -xzf /tmp/biomanager-server.tar.gz -C /opt/biomanager
    /opt/biomanager/Biomanager/deploy/host/load-image.sh

    You should see: Loaded image: ghcr.io/gaspolymerase/biomanager:<version> and Loaded. Start or update with: …

    If not: Could not download: the VM can't reach github.com. Ask IT about a proxy, then run the last line again.

  5. Put the certificate where BioManager reads it.

    cp ~/<certificate-file> /opt/biomanager/Biomanager/deploy/certs/server.crt
    cp ~/<key-file> /opt/biomanager/Biomanager/deploy/certs/server.key
    ls /opt/biomanager/Biomanager/deploy/certs

    You should see: server.crt server.key

    If not: No such file: check the file names with ls ~.

  6. Write the settings that need no choices.

    cd /opt/biomanager/Biomanager/deploy
    cp .env.example .env && chmod 600 .env
    sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 24)|" .env
    sed -i "s|^TLS=.*|TLS=files|; s|^BACKUP_DIR=.*|BACKUP_DIR=/opt/biomanager/backups|" .env

    You should see: no output.

  7. Enter your address and time zone: open the file, change the two lines, save with Ctrl+O, Enter, close with Ctrl+X.

    nano .env
    DOMAIN=<your-domain>
    TZ=<your-time-zone>

    <your-domain>: the DNS name from IT, without https://. <your-time-zone>: like Europe/Berlin or Asia/Shanghai.

    You should see: DOMAIN= near the top, TZ= about halfway down.

  8. Check the settings.

    grep -E '^(DOMAIN|TLS|TZ|BACKUP_DIR)=' .env
    docker compose config --quiet && echo "settings ok"

    You should see: your four lines, with TLS=files, then settings ok.

    If not: set POSTGRES_PASSWORD in .env: run step 6 again. DOMAIN=biomanager.example.edu: the edit wasn't saved; repeat step 7.

  9. Start BioManager.

    docker compose up -d --build

    You should see: biomanager-db-1, biomanager-app-1, biomanager-caddy-1 and biomanager-backup-1, each Started or Healthy.

    If not: pull access denied: run host/load-image.sh, then this again.

  10. Check that everything runs.

    docker compose ps

    You should see: db and app (healthy), caddy Up, backup (healthy) or, for a few minutes, (health: starting).

    If not: caddy restarting: docker compose logs caddy; usually the key doesn't match the certificate, or the files are swapped.

  11. Get the setup code for the first account.

    docker compose logs app | grep "setup code"

    You should see: No accounts yet. Create the first admin at /register with setup code 1a2b-3c4d-5e6f …

    If not: nothing: wait a minute and repeat.

  12. On your computer, open https://<your-domain>/register, fill in your name, a username, a password and the Setup code, and choose Create account. This first account is the lab's admin.

    You should see: you are signed in, and the lab's set-up questions open.

    If not: a certificate warning on some devices: ask IT for the certificate with its full chain, put it in server.crt again and run docker compose restart caddy.

  13. Turn on alerts, the watchdog and weekly updates.

    sudo host/install.sh

    You should see: Installed. Alerts go to ntfy topic: biomanager-…. Subscribe to it in the ntfy app.

    If not: command not found: cd /opt/biomanager/Biomanager/deploy first.

Done when: from a computer on the campus network or VPN, https://<your-domain>/healthz shows ok with no certificate warning, and you can sign in as the admin.

D · A cloud VM with Tailscale recommended

Your lab, anywhere$01 hour

The server has no open ports on the internet. Lab members open it at https://biomanager.tail1234.ts.net (your own tailnet name) from any network, with the Tailscale app on.

You need

  • An Oracle Cloud account. It asks for a card to verify you; Always Free resources are never charged. Pick your home region carefully: it can't be changed. (Or any provider's Ubuntu 24.04 VM with a cloud-init box: Hetzner about €4, DigitalOcean or AWS Lightsail about $5–7 a month.)
  • A Tailscale account (free for up to 6 users; share the server with anyone else's own free account).
  • Your computer, with a terminal and the Tailscale app.
  • Cost: $0 a month on Oracle Always Free.
  • Time: about an hour.
  1. On your computer, make an SSH key. Press Enter at each question.

    ssh-keygen -t ed25519

    You should see: Your public key has been saved in …/.ssh/id_ed25519.pub

    If not: … already exists. Overwrite (y/n)?: answer n and use the key you have.

  2. On your computer, download the server bundle and show its first-boot script.

    curl -fsSL -o biomanager-server.tar.gz https://github.com/gaspolymerase/biomanager/releases/latest/download/biomanager-server.tar.gz
    tar -xzf biomanager-server.tar.gz
    cat Biomanager/deploy/cloud-init.yaml

    You should see: a file that starts with #cloud-config. You paste all of it in step 3.

    If not: Windows PowerShell says a parameter can't be found: type curl.exe instead of curl.

  3. Create the VM: Oracle Cloud console → ☰ → Compute → Instances → Create instance, with:

    • Image: Change image → Ubuntu → Canonical Ubuntu 24.04.
    • Shape: Change shape → Ampere → VM.Standard.A1.Flex, 2 OCPUs, 12 GB memory.
    • Networking: a new virtual cloud network and public subnet; assign a public IPv4 address.
    • Add SSH keys: Upload public key files (.pub) → id_ed25519.pub from step 1.
    • Advanced options → Management → Initialization script → Paste cloud-init script: all of cloud-init.yaml from step 2.

    Then Create.

    You should see: the instance Running within a few minutes, with a Public IP address on its page. Note it: it's <public-ip> below.

    If not: Out of capacity for shape VM.Standard.A1.Flex: pick another availability domain under Placement, or try again later.

  4. On your computer, sign in to the VM. Answer yes to the fingerprint question.

    ssh ubuntu@<public-ip>

    You should see: a prompt that starts ubuntu@.

    If not: Connection timed out: the VM is still booting; try again in two minutes. Permission denied (publickey): ssh -i ~/.ssh/id_ed25519 ubuntu@<public-ip>.

  5. Wait for the first-boot script to finish (5–10 minutes after the VM starts).

    test -f /var/lib/biomanager-ready && echo ready

    You should see: ready

    If not: nothing: it's still running. sudo tail -f /var/log/cloud-init-output.log shows progress (Ctrl+C to stop watching). If that log never mentions Docker, the script wasn't pasted in step 3: terminate the VM and create it again.

  6. Put the VM on your Tailscale network.

    sudo tailscale up --hostname=biomanager

    You should see: To authenticate, visit: https://login.tailscale.com/a/…. Open that link on your computer and sign in; the terminal then says Success.

    If not: tailscale: command not found: step 5 isn't finished.

  7. In the Tailscale admin console: Machines → biomanager → ⋯ → Disable key expiry.

    You should see: Expiry disabled on the biomanager row.

    If not: it's called biomanager-1: an older machine has the name. Remove the old one (⋯ → Remove) and rename this one biomanager (⋯ → Edit machine name).

  8. In the admin console, DNS: enable MagicDNS, then under HTTPS Certificates choose Enable HTTPS. Note the Tailnet name on that page, like tail1234.ts.net.

    You should see: both on. Your server's address is biomanager. plus your tailnet name: that is <your-domain> below.

    If not: no HTTPS option: turn on MagicDNS first.

  9. On your computer, open the Tailscale app and sign in with the same account. Type exit in the VM's window, then sign in again through Tailscale.

    ssh ubuntu@biomanager

    You should see: the ubuntu@ prompt again. Use this connection from now on.

    If not: Could not resolve hostname biomanager: Tailscale is off on your computer, signed in to another account, or MagicDNS is off (step 8).

  10. Download BioManager.

    curl -fsSL -o /tmp/biomanager-server.tar.gz \
      https://github.com/gaspolymerase/biomanager/releases/latest/download/biomanager-server.tar.gz
    tar -xzf /tmp/biomanager-server.tar.gz -C /opt/biomanager
    /opt/biomanager/Biomanager/deploy/host/load-image.sh

    You should see: Downloading BioManager <version> for arm64…, then Loaded image: ghcr.io/gaspolymerase/biomanager:<version> and Loaded. Start or update with: …

    If not: permission denied while trying to connect to the Docker daemon socket: exit, ssh ubuntu@biomanager again, and repeat. Cannot mkdir: Permission denied: sudo chown ubuntu /opt/biomanager, and repeat.

  11. Write the settings that need no choices: a database password, Tailscale certificates and the backup folder.

    cd /opt/biomanager/Biomanager/deploy
    cp .env.example .env && chmod 600 .env
    sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 24)|" .env
    sed -i "s|^TLS=.*|TLS=tailscale|; s|^#COMPOSE_FILE=|COMPOSE_FILE=|; s|^BACKUP_DIR=.*|BACKUP_DIR=/opt/biomanager/backups|" .env

    You should see: no output.

  12. Enter your address and time zone: open the file, change the two lines, save with Ctrl+O, Enter, close with Ctrl+X.

    nano .env
    DOMAIN=<your-domain>
    TZ=<your-time-zone>

    <your-domain>: biomanager. and your tailnet name from step 8, like biomanager.tail1234.ts.net. <your-time-zone>: like Europe/Berlin or Asia/Shanghai.

    You should see: DOMAIN= near the top, TZ= about halfway down.

  13. Check the settings.

    grep -E '^(DOMAIN|TLS|COMPOSE_FILE|TZ|BACKUP_DIR)=' .env
    docker compose config --quiet && echo "settings ok"

    You should see: DOMAIN=biomanager.tail1234.ts.net (with your tailnet), TLS=tailscale, COMPOSE_FILE=compose.yaml:compose.tailscale.yaml, your TZ, BACKUP_DIR=/opt/biomanager/backups, then settings ok.

    If not: set POSTGRES_PASSWORD in .env or no COMPOSE_FILE line: run step 11 again. DOMAIN=biomanager.example.edu: the edit wasn't saved; repeat step 12.

  14. Start BioManager.

    docker compose up -d --build

    You should see: after a minute or two, biomanager-db-1, biomanager-app-1, biomanager-caddy-1 and biomanager-backup-1, each Started or Healthy.

    If not: pull access denied for ghcr.io/gaspolymerase/biomanager: run host/load-image.sh, then this again. container biomanager-app-1 is unhealthy: docker compose logs --tail 100 app says why.

  15. Check that everything runs.

    docker compose ps

    You should see: db and app (healthy), caddy Up, and backup (healthy), or (health: starting) for a few minutes while it takes its first backup.

    If not: a service missing or Restarting: docker compose logs --tail 100 <service>.

  16. Get the setup code for the first account.

    docker compose logs app | grep "setup code"

    You should see: No accounts yet. Create the first admin at /register with setup code 1a2b-3c4d-5e6f (also saved in /data/setup-code).

    If not: nothing: the app is still starting. Wait a minute and repeat.

  17. On your computer, with Tailscale on, open https://<your-domain>/register, fill in your name, a username, a password and the Setup code, and choose Create account. This first account is the lab's admin.

    You should see: you are signed in, and the lab's set-up questions open.

    If not: no answer or a certificate error: HTTPS isn't on in Tailscale (step 8) or the COMPOSE_FILE line is missing (step 11). Fix it, run docker compose up -d, and docker compose logs caddy if it still fails.

  18. Turn on alerts, the watchdog and weekly updates.

    sudo host/install.sh

    You should see: Installed. Alerts go to ntfy topic: biomanager-… and two timers, biomanager-watchdog.timer and biomanager-maintenance.timer. Subscribe to that topic in the ntfy app on your phone.

    If not: command not found: cd /opt/biomanager/Biomanager/deploy first.

  19. Close SSH to the internet: Oracle Cloud console → the instance → Subnet (under Primary VNIC) → Security Lists → Default Security List → Ingress Rules, tick the rule with destination port 22 → Remove. Then, on your computer:

    ssh -o ConnectTimeout=10 ubuntu@<public-ip>

    You should see: Connection timed out, while ssh ubuntu@biomanager still works.

    If not: it still connects: another rule opens port 22 (look for a network security group on the instance) and remove it too.

  20. Optional, recommended: keep Oracle from stopping the VM. Oracle Cloud console → Billing → Upgrade and Manage Payment → Upgrade to Pay As You Go. Always Free resources stay free.

    You should see: the account shown as Pay As You Go.

    If you skip this: Oracle stops an Always Free VM that looks idle for a week. Start it again in Compute → Instances; nothing is lost.

Done when: on your phone or computer with Tailscale on, https://<your-domain>/healthz shows ok, you can sign in at https://<your-domain> as the admin, and ssh ubuntu@<public-ip> times out.

E · A cloud VM with your own domain

Anyone with the addressAbout $1 a month1–2 hours

No app needed: anyone opens https://biomanager.yourlab.example. The sign-in page is on the open internet, so use strong passwords, and consider sign-in with Google or Microsoft.

You need

  • An Oracle Cloud account (free; a card verifies you; the home region can't be changed), or any provider's Ubuntu 24.04 VM with a cloud-init box.
  • A domain (about $10–15 a year) at a registrar where you can add DNS records, such as Cloudflare, Porkbun or Namecheap; or a subdomain from your department.
  • An email address for Let's Encrypt, which issues the certificate.
  • Your computer, with a terminal.
  • Cost: about $1 a month (the domain) on Oracle Always Free.
  • Time: 1–2 hours; a new DNS record can take up to an hour to work everywhere.
  1. On your computer, make an SSH key. Press Enter at each question.

    ssh-keygen -t ed25519

    You should see: Your public key has been saved in …/.ssh/id_ed25519.pub

    If not: … already exists. Overwrite (y/n)?: answer n and use the key you have.

  2. On your computer, download the server bundle and show its first-boot script.

    curl -fsSL -o biomanager-server.tar.gz https://github.com/gaspolymerase/biomanager/releases/latest/download/biomanager-server.tar.gz
    tar -xzf biomanager-server.tar.gz
    cat Biomanager/deploy/cloud-init.yaml

    You should see: a file that starts with #cloud-config. You paste all of it in step 3.

    If not: Windows PowerShell says a parameter can't be found: type curl.exe instead of curl.

  3. Create the VM: Oracle Cloud console → ☰ → Compute → Instances → Create instance, with:

    • Image: Change image → Ubuntu → Canonical Ubuntu 24.04.
    • Shape: Change shape → Ampere → VM.Standard.A1.Flex, 2 OCPUs, 12 GB memory.
    • Networking: a new virtual cloud network and public subnet; assign a public IPv4 address.
    • Add SSH keys: Upload public key files (.pub) → id_ed25519.pub from step 1.
    • Advanced options → Management → Initialization script → Paste cloud-init script: all of cloud-init.yaml from step 2.

    Then Create.

    You should see: the instance Running, with a Public IP address on its page: <public-ip> below.

    If not: Out of capacity for shape VM.Standard.A1.Flex: pick another availability domain under Placement, or try again later.

  4. Open ports 80 and 443: the instance → Subnet (under Primary VNIC) → Security Lists → Default Security List → Add Ingress Rules: source CIDR 0.0.0.0/0, IP protocol TCP, destination port 80; + Another Ingress Rule, the same with 443; Add Ingress Rules.

    You should see: two new rows, TCP 80 and TCP 443. Let's Encrypt needs port 80 to issue the certificate.

  5. At your registrar, add a DNS A record: name biomanager, value <public-ip>. Then, on your computer:

    nslookup <your-domain>

    <your-domain>: biomanager. and your domain, like biomanager.yourlab.example.

    You should see: Address: <public-ip> under the name.

    If not: no address yet: wait 10 minutes and repeat. On Cloudflare, set the record to DNS only (grey cloud), not proxied.

  6. Sign in to the VM. Answer yes to the fingerprint question.

    ssh ubuntu@<public-ip>

    You should see: a prompt that starts ubuntu@.

    If not: Connection timed out: try again in two minutes. Permission denied (publickey): ssh -i ~/.ssh/id_ed25519 ubuntu@<public-ip>.

  7. Wait for the first-boot script to finish (5–10 minutes after the VM starts).

    test -f /var/lib/biomanager-ready && echo ready

    You should see: ready. Then type exit and sign in again (step 6), so your account can use Docker.

    If not: nothing: sudo tail -f /var/log/cloud-init-output.log shows progress. If that log never mentions Docker, the script wasn't pasted in step 3: terminate the VM and create it again.

  8. Download BioManager.

    curl -fsSL -o /tmp/biomanager-server.tar.gz \
      https://github.com/gaspolymerase/biomanager/releases/latest/download/biomanager-server.tar.gz
    tar -xzf /tmp/biomanager-server.tar.gz -C /opt/biomanager
    /opt/biomanager/Biomanager/deploy/host/load-image.sh

    You should see: Loaded image: ghcr.io/gaspolymerase/biomanager:<version> and Loaded. Start or update with: …

    If not: permission denied while trying to connect to the Docker daemon socket: exit, sign in again, and repeat.

  9. Write the settings that need no choices.

    cd /opt/biomanager/Biomanager/deploy
    cp .env.example .env && chmod 600 .env
    sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 24)|" .env
    sed -i "s|^TLS=.*|TLS=acme|; s|^BACKUP_DIR=.*|BACKUP_DIR=/opt/biomanager/backups|" .env

    You should see: no output.

  10. Enter your address, email and time zone: open the file, change the three lines, save with Ctrl+O, Enter, close with Ctrl+X.

    nano .env
    DOMAIN=<your-domain>
    ACME_EMAIL=<your-email>
    TZ=<your-time-zone>

    <your-domain>: the name from step 5. <your-email>: Let's Encrypt writes there before a certificate expires. <your-time-zone>: like Europe/Berlin or Asia/Shanghai.

    You should see: DOMAIN= and ACME_EMAIL= near the top, TZ= about halfway down.

  11. Check the settings.

    grep -E '^(DOMAIN|TLS|ACME_EMAIL|TZ|BACKUP_DIR)=' .env
    docker compose config --quiet && echo "settings ok"

    You should see: your five lines, with TLS=acme, then settings ok.

    If not: set POSTGRES_PASSWORD in .env: run step 9 again. DOMAIN=biomanager.example.edu: the edit wasn't saved; repeat step 10.

  12. Start BioManager.

    docker compose up -d --build

    You should see: biomanager-db-1, biomanager-app-1, biomanager-caddy-1 and biomanager-backup-1, each Started or Healthy.

    If not: pull access denied: run host/load-image.sh, then this again.

  13. Check that Caddy got the certificate.

    docker compose logs caddy | grep "certificate obtained"

    You should see: a line with certificate obtained successfully and your domain.

    If not: nothing yet: wait a minute and repeat. Lines with challenge failed or timeout: the DNS record (step 5) or port 80 (step 4) isn't right yet. Fix it, then docker compose restart caddy.

  14. Get the setup code for the first account.

    docker compose logs app | grep "setup code"

    You should see: No accounts yet. Create the first admin at /register with setup code 1a2b-3c4d-5e6f …

    If not: nothing: wait a minute and repeat.

  15. Open https://<your-domain>/register, fill in your name, a username, a password and the Setup code, and choose Create account. This first account is the lab's admin.

    You should see: you are signed in, and the lab's set-up questions open.

    If not: That setup code is not right.: copy it again, all three groups and the dashes.

  16. Turn on alerts, the watchdog and weekly updates.

    sudo host/install.sh

    You should see: Installed. Alerts go to ntfy topic: biomanager-…. Subscribe to it in the ntfy app.

    If not: command not found: cd /opt/biomanager/Biomanager/deploy first.

Done when: on a phone on mobile data, https://<your-domain>/healthz shows ok with a padlock, and you can sign in at https://<your-domain> as the admin.

Bring the desktop app's records

Records, history and IDs all come along. Do this on a new server, before its first start: in route B, C, D or E, after Check the settings, do these steps instead of the rest of the route up to Turn on alerts. You then sign in with your existing account: no setup code.

  1. Quit the desktop app. Then, on that computer, copy its database and uploaded files to the server.

    scp "$HOME/Library/Application Support/Biomanager/data/biomanager.db" <you>@<server>:/tmp/lab.db
    scp -r "$HOME/Library/Application Support/Biomanager/uploads" <you>@<server>:/tmp/lab-uploads

    That is a Mac. On Windows the folder is $env:APPDATA\Biomanager (PowerShell), on Linux ~/.local/share/Biomanager. <you>@<server>: how you sign in to the server, like ubuntu@biomanager.

    You should see: each file copied at 100%.

    If not: uploads: No such file or directory: the app has no uploads. Skip the second line, and the second line of step 4.

  2. On the server, test the copy. It changes nothing.

    cd /opt/biomanager/Biomanager/deploy
    docker compose up -d db
    docker compose run --rm --no-deps -v /tmp/lab.db:/import/lab.db:ro app \
      sh -c 'python scripts/migrate-to-postgres.py --dry-run /import/lab.db "$DATABASE_URL"'

    You should see: copied : … rows in … tables, row counts verified and dry run : rolled back, nothing kept.

    If not: a list of values PostgreSQL would refuse: fix those records in the desktop app, then repeat step 1.

  3. Copy it for real.

    docker compose run --rm --no-deps -v /tmp/lab.db:/import/lab.db:ro app \
      sh -c 'python scripts/migrate-to-postgres.py /import/lab.db "$DATABASE_URL"'

    You should see: copied : … row counts verified, with no dry run line.

    If not: The copy failed and was rolled back: the server's database isn't empty (BioManager was started before). Ask on GitHub.

  4. Start BioManager and copy the uploaded files in.

    docker compose up -d --build
    docker compose cp /tmp/lab-uploads/. app:/data/uploads/

    You should see: the four services Started, then Successfully copied.

Done when: you sign in at https://<your-domain> with your desktop account and see your records. Then do your route's Turn on alerts step.

The same steps rebuild a lost server from a lab computer's copy: use the newest .db in its lab-copies/…/db folder and its uploads folder.

Other routes

Cloudflare Tunnel
An ordinary address like E, with no open port: cloudflared on the server connects out to Cloudflare, and Cloudflare Access can ask for a lab email login first. Not packaged; the AI guide has notes for it.
Without Docker
A Python web app (gunicorn) on PostgreSQL behind any HTTPS proxy: deploy/README.md, Without Docker. With Docker, backups, updates and restores are one command each.

Part three

Once it's running

After it's running

Each item links to its checklist in the runbook, which is also in the bundle as deploy/RUNBOOK.md. Security updates install nightly, and the containers refresh on Sundays.

Keeping the data safe

Several independent copies, so no single failure (a deleted VM, a dead disk, a stolen laptop, a bad update) loses the lab's records.

1 · Every nightThe server backs up the database and uploaded files, checks each backup can be read, and keeps the last 30. Automatic on every server.
2 · Every weekThe newest backup is restored into a scratch database and checked. Automatic.
3 · Off-siteEncrypted copies in cloud storage, kept 30 days, 12 weeks and 24 months; with the bucket's Object Lock on, safe even from someone who takes over the server. Backblaze B2's first 10 GB are free. sudo /opt/biomanager/Biomanager/deploy/host/offsite-setup.sh.
4 · On lab computersThe desktop app keeps its own copy of the whole lab: on the server, Settings → Copies of the lab on your computers → Make a key; in the desktop app, Settings → Keep a copy of your lab server, with the address and the key. It takes a fresh copy every day while open and keeps the last 14. An admin's Mac can also pull the server's nightly backups: deploy/mac/install.sh (how).
5 · EveryoneExport on every sheet gives a CSV; Settings → Export my data downloads your own records and notebook pages.
6 · Undo and historyEvery bulk change can be undone from Batches; the Audit log shows every edit, who made it and what it was before.

Restoring never deletes what it replaces: Restore a backup.

Questions

Is Oracle's free VM really free?
Always Free resources carry no charge; the card is for identity. Oracle may stop a free-tier VM that looks idle for a week: start it again and nothing is lost, or upgrade to Pay As You Go (route D, step 20) to stop it happening.
Is Tailscale safe for lab data?
Tailscale connects devices directly with WireGuard encryption and can't read the traffic. Anyone outside your tailnet can't see the server at all.
How big does the server need to be?
2 CPU cores, 2 GB of memory and 20 GB of disk. A lab with tens of thousands of animals uses well under 1 GB of database.
Can several labs share one server?
Run one BioManager per lab (each its own folder and domain), or one for a whole facility, where each lab keeps its own databases.
A step didn't work. Who can help?
Open an issue with your route, the step number and what it printed.