Welcome to gotochanger

A virtual tape library you can operate, break, and repair without touching real hardware.

gotochanger simulates a SCSI tape autochanger — storage slots, import/export (mail) slots, robotics, and multiple tape drives — behind a REST API and a web dashboard. It's most often used as a drop-in Changer Command target for Bareos, replacing a bare disk-staging setup with something that behaves like a real tape library: slots fill up, cartridges have real barcodes, drives can fault, and operations take realistic amounts of time if you turn latency simulation on.

This guide covers everything a day-to-day operator or administrator needs: the dashboard, the concepts behind slots/drives/tape sets, common step-by-step workflows, installation, the first-run setup wizard, Bareos integration, kernel mode, the Admin section, monitoring, the CLI and REST API, and a cookbook of full end-to-end scenarios.

By default, everything in gotochanger is simulated in userspace: no real kernel SCSI device is created, just plain files. Loads, unloads, faults, and even drive timing are all software state you can freely experiment with. An optional kernel mode can additionally expose the same library as real /dev/sg*//dev/nst* devices, for tools that insist on a real SCSI medium changer - see the Kernel Mode section for when that's actually needed.

Getting Started

First run: bootstrap and sign in

The first time you open gotochanger, you'll be asked to set a password for the built-in Admin account — there's no default password to guess. After that, sign in with username Admin and the password you just set. Sessions are cookie-based and held in memory by the daemon, so signing everyone out is as simple as restarting the service.

If this is a brand-new install with no topology configured yet (no drives, magazines, or mailboxes), you'll be dropped straight into the Setup Wizard instead of the dashboard.

Roles

Every user account and API token has exactly one role, checked on every request:

The dashboard adapts to your role automatically — action buttons (Load, Unload, Move, Raise fault, ...) only appear if you're an Operator or Admin, and the Admin nav button itself is hidden entirely for a Viewer.

Dashboard Tour

The dashboard is a set of independent panels, each mirroring one physical part of the library. Every panel can be dragged by its :: handle to reorder it, and collapsed with the button in its header — both the order and which panels are collapsed are remembered per-browser (saved to local storage), so your layout survives a reload. The toolbar above the panels has two more options: Show library colors (tints each element by which Logical Library it belongs to) and Hide unassigned (hide elements that aren't part of any Logical Library yet), plus a Collapse all shortcut.

The dashboard polls GET /api/v1/status every 4 seconds and re-renders automatically — you never need to manually refresh the page to see a change made from another tab, another operator, or the API.

Outside Library Tapes

Cartridges that exist as real files on disk but aren't in any slot, drive, or the offsite vault right now — think of it as the loading dock. This is also where new cartridges are born: the Create tape button walks you through picking a Tape Set and either auto-generating the next barcode in sequence or typing one in by hand. From here, tapes get loaded into a drive, a storage slot, or an I/O slot.

Offsite Vault

Volumes that have been sent offsite — simulating tape rotation to a physical vault. Send to offsite picks a full storage slot to vault; each vaulted volume gets a Recall button to bring it back into an empty storage slot. See Offsite Vaulting for the concept and how scheduled rotation works, and the Scheduled offsite rotation cookbook scenario for a worked example with the CLI/API.

Robotic Arm

There's exactly one robotic arm in the physical library, shared by every Logical Library. This panel shows its status — idle, moving, or in a simulated fault — with an indicator light using the same convention as the drives (see Drive Indicator Lights). Operators can Raise fault (choosing a realistic failure mode: blocked arm, mispositioned cartridge, pickup/drop failure, movement jam, or other) to test how their backup software reacts, then Clear fault once done. A raised fault rejects Load/Unload/Move (but not door open/close) library-wide until cleared — see the Bareos resilience testing cookbook scenario for a full walkthrough with events and SNMP traps.

Drives

Every physical tape drive, each with an indicator light (see below), the cartridge currently loaded (if any) rendered as a real-looking barcode label, and how full it is. Loaded drives get an Unload to... button; every drive gets a Raise fault / Clear fault toggle to simulate a hardware problem on that specific drive.

Drive Indicator Lights

Each drive (and the robotic arm) shows a small colored light modeled on a real tape drive's front panel, so the dashboard reads at a glance instead of requiring you to parse text on every card.

Amber, pulsingFault - the drive (or the robotic arm) is in a simulated fault state. Takes priority over everything else.
Red, pulsingWriting - the loaded cartridge's backing file just grew between two dashboard polls, meaning something is actually writing data to it right now (e.g. a real attached backup job).
Green, blinkingActive operation - this browser tab has a Load/Unload/Move in flight for this drive (or the arm generally, for the Robotic Arm panel).
Green, steady/dimReady - a cartridge is loaded and idle.
DarkEmpty - no cartridge loaded, nothing happening.

Priority order when more than one condition is true, highest first: Fault > Writing > Active operation > Ready > Empty. A faulted drive always shows amber even mid-operation, for instance.

The "Writing" light needs a real write, not just a Load. gotochanger doesn't simulate the byte stream of a backup job — a loaded drive (in userspace/file mode) is just a symlink at a device path, and nothing writes to it unless something external actually does (typically a real attached Bareos SD job, or a manual test write). So clicking Load and watching the drive sit at steady green with no red flash is completely normal, not a bug — it just means nothing has written any bytes to that cartridge yet.

Two more details worth knowing if you're timing things precisely: the "busy" light only reflects an operation this browser tab started — there's no way to see another tab's or another operator's in-flight action as "busy" (it just means an operation is happening somewhere; a status refresh from any tab will briefly pause until it finishes, since only one physical robotic arm exists). And the "writing" light, once triggered, stays lit for a few seconds after the write is detected (longer than one poll interval) so a brief burst of activity remains visible instead of flickering for a single frame.

I/O Slots (mail slots)

Import/export elements, grouped into Mailboxes. Each mailbox group has its own door: click Open mail slot to start staging tapes in or out, then use each slot's Load (bring an outside tape in) or Pickup (queue a tape to come out) button, and finally Close mail slot to commit every queued action at once — just like closing a real mail slot door triggers the robot to do the actual moves.

Storage Slots

The main body of the library, grouped into Magazines. Each occupied slot can Move its cartridge to an empty drive, another slot, or an I/O slot. Open a magazine's storage door to get Bulk load... (load several outside tapes into empty slots in one dialog) and Move all to outside... (queue several loaded slots for pickup at once) — handy for seeding a fresh magazine or clearing one out.

Below the panels, the Activity Log dock (bottom of the screen, also collapsible) shows a running feed of everything that's happened — loads, unloads, faults, configuration changes — each tagged with a status pill (success/failure/warning/...), useful for spotting what an automated job or another operator just did.

Core Concepts

gotochanger's data model mirrors a real tape library fairly literally. If you've operated a physical autochanger or an LTO library before, most of this will already be familiar.

Magazines & Storage Slots

A Magazine is a named group of 5-20 storage slots (in increments of 5) — the removable cartridge racks a real library holds internally. Storage slots are what hold the bulk of your tape inventory between backup jobs. Slots are addressed contiguously across the whole physical library (all magazines' slots first, in creation order), which only matters if you're scripting against the raw API — the dashboard always shows you slots grouped back into their magazine, and each slot also gets a human-facing, magazine-relative label like 2.3 (slot 3 of the 2nd currently-existing magazine).

Mailboxes & I/O Slots

A Mailbox is the import/export equivalent of a magazine — a named group of 1-5 I/O (mail) slots. I/O slots share one contiguous address space with storage slots (storage slots first, then I/O slots), matching how real SCSI medium changers and Bareos itself report a combined "N slots (M import/export)" total. This is the normal door operators use to bring new cartridges in or send full ones out without opening the whole library.

Tape Drives

Each physical tape drive (a "Data Transfer Element") can hold at most one cartridge at a time. A drive can be individually put into a simulated fault state to test failure handling, independent of every other drive and of the robotic arm. By default a loaded drive is a plain symlink at a configured device path (userspace/file mode); Kernel Mode can instead back it with a real /dev/nst* device.

Tape Sets & Barcodes

Every cartridge belongs to exactly one Tape Set — a named group of cartridges that share a Tape Type (the media family: LTO, DLT, SDLT, DDS/DAT, AIT/SAIT, IBM 3592, or a non-physical "generic" type) and a storage folder on disk. A cartridge's barcode is its one and only identifier — there's no separate "volume label" concept — and it's always unique across the entire library, not just within its own tape set. Barcodes auto-generate in sequence per tape type (skipping past any already in use), or you can type one in by hand as long as it matches the tape type's format.

Family Shape Example Notes
LTO 6-digit volser + 2-char media id 000001L8 Media id like L8/L9 per LTO generation; real published vendor format.
DLT 6-digit volser + 0-1 char media id 0000034 7 characters total for DLT-IV; real published vendor format.
SDLT 6-digit volser + 1-2 char media id 000007S2 Real published vendor format.
DDS / AIT / 3592 6-digit volser + 2-char media id 000001D6 No official external barcode standard for these - gotochanger's own convention, for consistency with LTO/SDLT.
Generic Configurable length, no media id 00000001 Used by the built-in "Unlimited"-capacity type, for non-physical/test tapes.
Cleaning Fixed 5-digit sequence + CLN suffix 00001CLN Not admin-configurable; see Managing cleaning tapes.

Barcodes render as real Code 39 bars throughout the dashboard (the same symbology real tape libraries and barcode scanners use) rather than as plain text — purely cosmetic, but it makes a slot full of cartridges look like an actual tape library.

Logical Libraries

A Logical Library partitions the physical library into an independent slice — its own subset of drives, magazines, and mailboxes — the same way a Dell ML3 or similar real library can be split into two logical autochangers sharing one chassis. Each drive/magazine/mailbox can belong to at most one Logical Library at a time (an element already assigned to one can't be added to another). This is what lets one gotochangerd instance stand in for two completely separate Bareos Autochanger resources - see Partitioning one physical robot into two logical libraries for a full worked example. Elements not yet assigned to any Logical Library show up under Admin > Logical Libraries > Unassigned.

Offsite Vaulting

Simulates rotating full tapes to a physical offsite vault: sending a volume moves it out of its storage slot into the vault; recalling brings it back into an empty slot. This can also happen on a schedule (Admin > Settings > Offsite rotation) — a chosen number of full volumes get sent offsite automatically at a configured interval, simulating routine tape rotation without a human doing it by hand every time. See Scheduled offsite rotation for the CLI/cron-driven version.

Installation

gotochanger ships as two Debian binary packages built from the same source tree - gotochanger (the daemon, the Bareos changer shim, and the admin CLI) and the optional gotochanger-kernel add-on (see Kernel Mode) - plus a Docker image covering the gotochanger package's contents (no gotochanger-kernel image exists yet, see Run with Docker below for why). All paths below produce the same binaries; pick whichever fits your environment.

Install from a .deb package

Pre-built .deb packages for Debian 13 (trixie) (gotochanger and the optional gotochanger-kernel add-on) are published to an apt repository after every release:

sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://apt.sw-servers.net/apt-sw-servers.net.gpg.asc | sudo gpg --dearmor -o /etc/apt/keyrings/gotochanger.gpg
echo "deb [signed-by=/etc/apt/keyrings/gotochanger.gpg] https://apt.sw-servers.net/gotochanger trixie main" \
  | sudo tee /etc/apt/sources.list.d/gotochanger.list
sudo apt-get update
sudo apt-get install gotochanger
# optional, only if this deployment needs real /dev/sg*/dev/nst* devices:
sudo apt-get install gotochanger-kernel

Every tagged release also publishes both .debs (and plain binary tarballs) as GitHub Releases assets - installable directly with sudo apt install ./gotochanger_<version>_amd64.deb without configuring a repository at all.

Build from source

make build            # binaries land in ./bin
make test             # go test ./...
sudo make install DESTDIR=/some/root   # same install layout debian/rules uses

Dependencies are vendored (vendor/), so this never needs network access. make build also regenerates the embedded User Guide from docs/guide/**.md first (make guide) - see CLI Reference and REST API if you're editing the guide itself and want to preview it with make site before publishing.

Build the Debian package directly

dpkg-buildpackage -us -uc -b

Produces the same two binary packages (gotochanger, gotochanger-kernel) from a plain git checkout, no network access required.

Run with Docker

A Docker image covering the gotochanger package's contents (gotochangerd, gotochanger-changer, gotochangerctl) is published to Docker Hub after every release:

docker pull swenske/gotochanger:latest
docker run -d --name gotochanger \
  -p 8480:8480 \
  -v gotochanger-data:/var/lib/gotochanger \
  swenske/gotochanger:latest

-v gotochanger-data:/var/lib/gotochanger persists state.db (topology, users, tokens, volumes - everything except data_dir/listen, which come from the config file baked into the image) across container restarts. The web UI is then reachable at http://<host>:8480/ - continue with First Run and Setup Wizard.

Only linux/amd64 is published, matching every other release artifact. There is no gotochanger-kernel image: gotochanger-tcmud needs real host kernel/TCMU access, and gotochangerd's kernel-mode reconciler manages it via systemctl talking to a real host systemd/polkit - neither is available inside a plain container, so kernel mode currently requires a .deb install (see Kernel Mode).

To read the one-time bootstrap admin API token (same as the systemd/journalctl flow below, just via docker logs):

docker logs gotochanger 2>&1 | grep 'bootstrap API token'

The admin CLI is included in the image for one-off commands. Against the same container's trusted Unix socket:

docker exec gotochanger gotochangerctl status

Or, from anywhere, against a remote gotochangerd (--url/--token, see CLI Reference and REST API):

docker run --rm --entrypoint gotochangerctl swenske/gotochanger \
  --url http://<host>:8480 --token <api-token> status

First start

sudo systemctl status gotochanger
journalctl -u gotochanger | grep 'bootstrap API token'   # save this - it's an admin-scoped token, shown once

Then open the web UI at http://<host>:8480/ (or whatever listen.http is set to in /etc/gotochanger/config.yaml) and continue with First Run and Setup Wizard.

First Run and Setup Wizard

A fresh install has no drives, magazines, or mailboxes configured at all - the daemon starts completely empty and walks you through bootstrap, then an 8-step wizard, before handing you the dashboard.

Bootstrap

The very first request to the web UI (or POST /api/v1/auth/bootstrap) sets the password for the built-in Admin account - there's no default password to guess, and this only works once (GET /api/v1/auth/state reports whether bootstrap is still required). After that, sign in normally; sessions are cookie-based and held in memory only, so restarting gotochangerd signs everyone out.

The wizard

You can always go back with Previous without losing what you've already entered, and everything you submit is saved immediately to the database (not just at the end) - closing the browser mid-wizard never loses earlier steps. gotochangerctl wizard status reports the current step from the CLI; the wizard itself is only driven through the web UI/REST API (GET/POST /api/v1/wizard, POST /api/v1/wizard/complete, POST /api/v1/wizard/reset, GET /api/v1/wizard/options).

  1. Operational Mode - name your virtual tape library (the VTL name shown throughout the UI) and pick the operational mode: changer (default userspace/file mode) or kernel (see Kernel Mode).
  2. Drives - choose how many physical drives to create and which drive type each one is, from the drive-type catalog.
  3. Magazines - define one or more magazines (storage slot groups, 5-20 slots each).
  4. Mailboxes - define one or more mailboxes (I/O slot groups, 1-5 slots each) - optional, a library can run with no I/O slots at all.
  5. Offsite Location - a simple on/off toggle for whether offsite vaulting is available at all; the rotation schedule itself is configured later, in Admin > Settings.
  6. Tape Sets - create at least one tape set (tape type + storage folder + how many cartridges to generate up front). The cartridge-count field is consumed once, at step 8's completion.
  7. Logical Libraries - optionally partition your drives/magazines/mailboxes into one or more Logical Libraries; you can also leave everything unassigned and do this later from Admin.
  8. Latency Simulation - a single checkbox: enable realistic timing delays or not. The actual delay values always start at sensible factory defaults and are tuned afterwards from Admin > Latency, never from the wizard itself.

POST /api/v1/wizard/complete hot-applies everything to the running daemon immediately - no restart, and the dashboard reflects your new topology the instant you land on it. It also generates each tape set's pending cartridges and reconciles any kernel-mode instances if operational mode is kernel.

The wizard is resumable across a daemon restart: every step's data is written to the database as you submit it, so a restart mid-wizard picks up exactly where you left off instead of starting over.

Bareos Integration

Point Bareos's Device resource at the changer shim exactly like disk-changer.in, and set Device Type = File with Archive Device matching the corresponding entry in library.drive_devices.

Autochanger and Device resources

Autochanger {
  Name = FakeML3
  Device = Drive0, Drive1
  Changer Device = /dev/null              # unused, kept for compatibility
  Changer Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V"
}

Device {
  Name = Drive0
  Drive Index = 0                         # required with 2+ drives - see below
  Media Type = File
  Archive Device = /var/lib/gotochanger/drives/drive0
  Device Type = File
  AutomaticMount = yes
  RemovableMedia = yes
  AutoChanger = yes
}

Device {
  Name = Drive1
  Drive Index = 1
  Media Type = File
  Archive Device = /var/lib/gotochanger/drives/drive1
  Device Type = File
  AutomaticMount = yes
  RemovableMedia = yes
  AutoChanger = yes
}

Add the bareos system user to the gotochanger group so it can reach the trusted local socket and read/ write volume files:

sudo adduser bareos gotochanger

Supported changer commands (matching disk-changer.in): load, unload, list, listall, slots, loaded, transfer. Extra commands usable by hand (never invoked by Bareos itself): outside, outside-delete, io-door, storage-door, ioslots, offsite-send, offsite-recall.

The Drive Index trap. Drive Index defaults to 0 for any Device resource that doesn't set it explicitly - with only one drive that's harmless, but with two or more it means every drive silently collapses to "drive 0" from Bareos's point of view (regardless of the Device's Name/Archive Device), so it's required as soon as an Autochanger has more than one Device. Set it to the drive's 0-based position within this Autochanger's own Device = list, not gotochangerd's own drive index (they happen to match here only because both start at 0 and are contiguous). See the Configure multiple drives cookbook scenario for a worked example, including how to catch this if it's already happened to you.

The Admin UI's Logical Libraries "Bareos Config" button generates this block correctly, including Drive Index, for whatever drives are actually assigned to that logical library - the fastest way to get a correct config skeleton without hand-counting indices.

Scoping an Autochanger to one logical library

If the physical library is partitioned into multiple logical libraries, add a static --logical-library=NAME flag to that Autochanger resource's Changer Command line (Bareos has no substitution variable for this, so it's a fixed per-Autochanger suffix - the Autochanger is already permanently bound to one logical library):

Changer Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V --logical-library=Library1"

With this set, load/unload/move (and their X-Logical-Library REST/CLI equivalents) are rejected with an error if the addressed slot, I/O slot, or drive doesn't belong to Library1 - this is what keeps two Bareos Autochangers sharing one physical gotochanger instance from touching each other's media. See Partition one physical robot into two logical libraries for a full worked example with two separate Bareos Storage Daemons.

Migrating from disk-changer.in

gotochanger is designed as a drop-in replacement: it stays compatible with existing Device Type = File Bareos configurations, so an existing disk-changer.in-based Autochanger can usually switch over by changing only the Changer Command line - Device/Media Type/Archive Device stay the same. See Migrate from disk-changer.in for the full step-by-step migration, including how to verify Bareos still sees the same volumes in the same slots afterward.

Kernel Mode

By default gotochanger runs in userspace/file mode: loading a drive symlinks a plain file at the configured Archive Device path, no root or kernel modules required. An optional kernel mode instead exposes the same library as real SCSI devices (/dev/sg* for the changer/generic device, /dev/nst* for tape drives) via TCMU/LIO (target_core_user), for tools that insist on talking to an actual SCSI medium changer rather than a changer-script convention - no real tape hardware is involved either way, kernel mode just adds a real kernel device node backed by the same plain files.

When to enable it

Bareos itself never needs kernel mode - it talks to gotochanger-changer as a Changer Command script and reads/writes plain files either way. Turn kernel mode on only when something else in your stack (a third-party backup tool, a monitoring agent, a test harness) specifically requires a real /dev/sg*//dev/nst* device path rather than a script-driven changer convention. See Switch to kernel mode for a third-party SCSI tool for a full worked example.

Enabling it

  1. Install the separate gotochanger-kernel package (depends on gotochanger and polkitd). It needs root and the target_core_user kernel module - the package's postinst does a best-effort modprobe.
  2. Turn it on by setting the operational mode to kernel (setup wizard, or Admin > Settings / gotochangerctl settings set operational_mode=kernel). gotochangerd's own reconciler then automatically starts/stops one gotochanger-tcmud@<logical-library-name>.service instance per logical library (@default if the library is unscoped) via polkit-authorized systemd calls - no manual systemctl enable step is required, though it's supported (the Admin UI's per-library "Kernel Mode Setup" dialog shows the equivalent manual systemctl enable --now gotochanger-tcmud@<instance> command for cases where automatic management isn't wanted).
  3. Real devices then appear under /dev/sg*//dev/nst*. Prefer the stable /dev/tape/by-id/scsi-<NAA>[-nst] symlinks over raw /dev/sgN//dev/nstN numbers, which are not stable across a gotochanger-tcmud restart. Admin > Drives and the Bareos-Config generator button both show the actual current device paths.
  4. Point the third-party tool's device configuration at those real device paths instead of the file-based ones - for Bareos specifically, no other config-file syntax change is needed either way.

Device paths are tracked in memory on the gotochangerd side (a running gotochanger-tcmud self-reports them at startup via POST /api/v1/kernel-mode/devices/{instance}) and are lost on a gotochangerd restart until the gotochanger-tcmud instance itself restarts.

Reporting a real vendor/product SCSI identity

By default every kernel-mode device reports gotochanger's own identity (GOTOCHNG/Virtual LTO-9/ Virtual Changer, etc.) in its SCSI INQUIRY response - fine for Bareos and most third-party tools, which don't care. A tool that whitelists specific real vendor/product strings needs the real thing instead, so this is opt-in per catalog entry:

Changing either setting takes effect the next time the affected gotochanger-tcmud instance (re)starts, not live against an already-running device.

Multi-partition tapes (kernel mode only)

A drive in kernel mode can format a mounted volume with two SSC partitions instead of one - the layout LTFS itself needs (a small index partition plus a large data partition). Nothing in userspace/file mode or the Admin UI creates a second partition; it's set purely via real SCSI commands (MODE SELECT staging a partition count via the Medium Partition mode page, FORMAT MEDIUM applying it, LOCATE(16) or LOCATE(10)'s CP bit switching between partitions). Each partition beyond the first is a fully independent backing file next to the volume's own (<path>.p1), so data written to one partition never leaks into the other. Only two partitions total are supported - enough for LTFS's own convention, not arbitrary partitioning. The resulting partition count is visible read-only wherever the Admin UI/dashboard shows that cartridge, as a "2P" badge on its card (hover the card for the full detail line).

Cartridge memory (MAM) attributes (kernel mode only)

A drive in kernel mode answers real SCSI READ ATTRIBUTE/WRITE ATTRIBUTE commands against the mounted volume's MAM (Medium Auxiliary Memory) - the small chip embedded in a real cartridge that stores its own identity, capacity, and application-set metadata. Only a focused subset of the full T10 attribute table is implemented: remaining/maximum capacity, TapeAlert flags, load count, and volume identifier (all read-only, derived from state gotochanger already tracks), plus application vendor/name/version and a user medium text label (read/write - genuinely persisted on the volume, surviving unmount/remount, settable only via a real WRITE ATTRIBUTE command). Verify with sg_read_attr/sg_write_attr (sg3-utils) against the drive's /dev/sg* device. Load count and the mutable attributes above are visible read-only in the Admin UI/ dashboard, in the hover tooltip on that cartridge's card.

Real tape encryption (kernel mode only)

A drive in kernel mode implements real SCSI Security Protocol In/Out (SPIN/SPOUT) tape encryption - the same "Tape Data Encryption" protocol real LTO encrypting drives and key-manager software (e.g. stenc) use. Setting an AES-256 key and turning encryption on (via stenc -e on -k <keyfile>, or any T10-compliant key manager) makes every subsequent WRITE(6) genuinely AES-256-GCM-encrypt its data before it touches the backing file, and every READ(6) genuinely decrypt it back - not a protocol-only stub. The key is session-scoped: it must be re-supplied after every drive load/unload or gotochanger-tcmud restart, exactly like real hardware - there is nothing to configure in advance, and no key is ever persisted by gotochanger itself. Reading previously-encrypted data with the wrong key (or no key at all) correctly fails with a Data Protect/Logical Unit Access Not Authorized SCSI error rather than returning garbage. Encryption is decided once, at the start of a fresh recording pass (BOT) - this project has no concept of part-encrypted, part-plain data on one volume. Once set, the volume's encrypted flag is visible read-only wherever the Admin UI/dashboard shows that cartridge, as an "Encrypted" badge on its card.

Scope and limitations

See gotochangerctl status commands and GET /api/v1/kernel-mode/status / GET /api/v1/kernel-mode/devices to check whether the kernel module and any running instances are currently available.

Administration

The Admin section is organized into three groups, in the order you'll typically need them: who can get in, what the library is made of, and how it behaves day to day. Every route under Admin is Admin-only - there is no Operator-reachable exception, since these actions change shared topology, credentials, or the database itself.

Access Control

Users - create additional accounts beyond the built-in Admin, each with one of the three roles (Viewer/Operator/Admin). Passwords are hashed with PBKDF2-HMAC-SHA256; a locked-out account (5 failed logins) unlocks itself automatically after 15 minutes. The last remaining Admin account can never be deleted or demoted, so you can't accidentally lock yourself out entirely.

API Tokens - role-scoped tokens for scripts/automation to authenticate with, via either an X-Api-Key header or an Authorization: Bearer token. Managing tokens is Admin-only, but a token itself can be scoped down to Viewer or Operator - see Set up a scoped operator API token for a CI script for a full example. Only a token's SHA-256 hash is ever stored; a token's raw value is shown exactly once, at creation time.

The bootstrap install also auto-generates a single admin-scoped token on first run, logged once to journalctl -u gotochanger (grep 'bootstrap API token') - useful for scripting an initial setup before any user account exists.

Library Topology

Everything that defines the physical (and logical) shape of the library. All of it hot-applies immediately - no daemon restart, ever - when you add, edit, or remove something here.

Operations

Monitoring

Every state-changing action in gotochanger emits two things: one activity-log event (visible via GET /api/v1/events, the dashboard's Activity Log dock, and gotochangerctl events), and - when SNMP is enabled - one SNMPv2c trap. This includes both success and failure outcomes for robotics/media actions, authentication actions (login/logout/bootstrap/password change), and configuration actions (users/tokens/settings/scheduled backups).

Event codes

Events use a structured code taxonomy, for example:

Each event also carries category, severity, outcome, and operation fields (plus a free-form detail string) so operators and NMS rules can classify behavior without parsing free text.

Domain Code prefix Typical severity
Robotics/media movement ROBOTICS.*, MEDIA.* information / warning
Drive state DRIVE.* information / error
Authentication AUTH.* information / error
Configuration/admin CONFIG.* configuration / error
Cleaning cycles CLEANING.* information / warning
Internal daemon failures SYSTEM.* error

Outcome suffix convention: *.SUCCESS for completed actions, *.FAILURE for rejected/failed actions, *.WARNING for non-fatal alerts (for example simulated end-of-tape).

SNMP traps

Disabled by default. Like the rest of the daemon's configuration, SNMP settings live in the database and are edited live, with no config file and no restart required:

The dynamic MIB

A MIB matching your currently configured enterprise OID is served at GET /api/v1/snmp/mib (any authenticated Viewer+ role) and linked from Admin > Settings > SNMP traps. The endpoint rewrites the PEN and root object-identifier lines of a bundled MIB template to match the live snmp.enterprise_oid setting, so whatever you load into your NMS always decodes the exact OIDs this instance actually emits - even after changing the enterprise OID. See Monitor gotochanger via SNMP for a full worked example: receiving a trap, downloading the MIB, and decoding one in a real monitoring tool.

The REST API's OpenAPI spec (served at /api/v1/openapi.json, rendered at /docs) currently documents the most commonly used routes but not every admin/topology endpoint listed in CLI Reference and REST API - treat the CLI reference and this guide as the source of truth for anything not yet in Swagger.

Prometheus metrics

Disabled by default, like SNMP. Enable it from Admin > Settings > "Prometheus" (Enable checkbox, current status, and a "Download Grafana dashboard" button), or from the CLI:

gotochangerctl prometheus enable

Once enabled, GET /metrics serves metrics in the standard Prometheus text exposition format.

/metrics is intentionally unauthenticated - matching standard Prometheus scrape practice, and reachable even on the authenticated TCP listener with no session cookie or API token. Restrict network access to it (firewall, reverse proxy, or a scrape-only security group) if this daemon is reachable beyond trusted monitoring infrastructure: it exposes slot/volume/tape-set naming and library topology to anyone who can reach it, even though it never exposes credentials.

Example Prometheus scrape config:

scrape_configs:
  - job_name: 'gotochanger'
    static_configs:
      - targets: ['localhost:8480']  # adjust host:port to your listen address

Metrics are recorded regardless of transport: both the TCP API and the trusted Unix socket (used by gotochanger-changer/gotochangerctl/gotochanger-tcmud - i.e. how Bareos actually drives this daemon) share the same routing, so gotochanger_operations_total/gotochanger_operation_duration_seconds reflect real Bareos-driven activity, not just direct API calls.

Metric Type Labels Description
gotochanger_slots_total gauge Total storage slots
gotochanger_slots_free gauge Free storage slots
gotochanger_slots_occupied gauge Occupied storage slots
gotochanger_readers_total gauge Total tape drives
gotochanger_readers_idle gauge Drives loaded but not currently reading/writing
gotochanger_readers_active gauge Drives currently reading or writing
gotochanger_readers_free gauge Drives with no volume loaded
gotochanger_readers_error gauge Drives in a simulated fault state
gotochanger_volumes_total gauge Total tape volumes known to the library
gotochanger_volumes_by_status gauge status (in_slot, in_ioslot, in_drive, outside, offsite) Volumes by current location
gotochanger_magazines_total gauge Total storage magazines
gotochanger_capacity_utilization_percent gauge Occupied storage slots as a percentage of total
gotochanger_queue_depth gauge 1 if the single robotic arm is currently busy, 0 if idle - this simulator has one arm and no operation queue
gotochanger_uptime_seconds gauge Seconds since the daemon started
gotochanger_last_backup_timestamp gauge Unix timestamp of the last configuration backup (Admin > Backup, a state.db snapshot) - 0 if none has ever been taken. Not a Bareos backup-job signal; this daemon only models the changer/library, not Bareos jobs
gotochanger_operations_total counter operation_type (load, unload, move, door_open, door_close, offsite_send, offsite_recall) Total library operations executed, whether they succeeded or failed
gotochanger_operation_duration_seconds histogram operation_type Library operation latency in seconds
gotochanger_errors_total counter error_type (bad_request, unauthorized, forbidden, not_found, conflict, internal, other) Total request errors, bucketed by HTTP status class

A ready-to-import Grafana dashboard (Overview, Storage Capacity, Reader Status, Tape Inventory, Operations Timeline, and System Health rows, with threshold-based coloring on capacity/error-rate panels) is available from Admin > Settings > Prometheus > "Download Grafana dashboard", or gotochangerctl prometheus dashboard gotochanger-dashboard.json. See Monitor gotochanger via Prometheus and Grafana for a full worked example: enabling the exporter, scraping it, and importing the dashboard.

CLI and REST API Reference

Every action available in the web UI is also available over the gotochangerctl CLI and the REST API - the dashboard is a client of the same API, nothing more.

gotochangerctl

By default, gotochangerctl talks to the trusted local Unix socket (/run/gotochanger/gotochanger.sock, no token needed - every request over it is treated as Admin). Use --url/--token to drive a remote instance instead, --json for machine-readable output, and --logical-library=NAME to scope status/load/unload/move to one logical library.

gotochangerctl status                                        # arm/drive/slot/ioslot snapshot
gotochangerctl events                                         # recent event log
gotochangerctl volumes                                        # racked volumes (slots/ioslots/drives)
gotochangerctl outside                                        # outside-library ("loading dock") volumes
gotochangerctl load <slot|ioslot> <address|label> <drive>      # move a volume into a drive
gotochangerctl unload <drive> <slot|ioslot> <address|label>
gotochangerctl move <slot|ioslot> <addr> <slot|ioslot> <addr>
gotochangerctl outside-delete <barcode>                        # remove an outside-library cartridge
gotochangerctl io-door <mailbox-id> open [pin] | close [actions-json]
gotochangerctl storage-door <magazine-id> open [pin] | close [actions-json]
gotochangerctl fault <drive> <on|off>                          # simulate a drive fault
gotochangerctl write-protect <barcode> <on|off>
gotochangerctl robotic-fault on <kind> [message] | off         # simulate a robotic-arm fault
gotochangerctl token new|revoke|list [name] [role]             # API token management (admin/operator/viewer)
gotochangerctl user new|list|delete|role|reset-password ...    # local user accounts
gotochangerctl settings get | set <key>=<value> ...            # daemon-wide settings (snmp, offsite, ...)
gotochangerctl latency get | set <k>=<v>... | reset            # simulated latency knobs
gotochangerctl prometheus status | enable | disable            # Prometheus /metrics exporter toggle
gotochangerctl prometheus dashboard <output-file>               # download the Grafana dashboard JSON
gotochangerctl cleaning settings get|set|reset                 # cleaning tunables
gotochangerctl cleaning tape new|list                          # create/list cleaning cartridges
gotochangerctl logical-library new|list|show|update|delete ...
gotochangerctl drive-type new|list|update|delete ...
gotochangerctl tape-type new|list|update|delete ...
gotochangerctl tape-set new|list|update|delete|add-tapes|add-tape ...
gotochangerctl magazine new|list|update|delete ...
gotochangerctl mailbox new|list|update|delete ...
gotochangerctl drive new|list|update|delete ...
gotochangerctl unassigned                                      # drives/slots/ioslots not in any logical library
gotochangerctl offsite list|send|recall ...
gotochangerctl backup download|list|download-stored|delete|schedule show|schedule set ...
gotochangerctl restore <file>                                   # replaces state.db (incl. users/tokens!) + restarts
gotochangerctl reset <confirm-name> [--delete-volumes]            # factory-reset + restart
gotochangerctl wizard status                                      # setup-wizard state (the wizard itself is web/API only)

<address|label> arguments accept either a bare physical integer address or the human-facing "<ordinal>.<offset>" label (e.g. "2.3" - slot 3 of the 2nd currently-existing magazine).

outside-create, import, create-volume, and export are retired - creating a new cartridge is now always tape-set add-tape (it belongs to a tape set), and moving media in/out of the library is always the io-door/storage-door open/queue/close workflow described in Dashboard Tour.

REST API

All actions are available over HTTP. The Unix socket is trusted (every request is treated as Admin, access controlled by filesystem permissions); the TCP listener accepts either a browser session cookie or an API token (X-Api-Key header or Authorization: Bearer <token>), each carrying one of the admin/operator/viewer roles. Two routes outside /api/v1 are deliberately unauthenticated: GET /healthz (liveness) and GET /metrics (Prometheus text exposition, 404 while the exporter is disabled - see Monitoring).

Method Path Role Purpose
POST /api/v1/auth/bootstrap none Set the initial Admin password
POST /api/v1/auth/login none Log in, start a session
POST /api/v1/auth/logout viewer+ Log out
GET /api/v1/auth/state none Am I logged in? Is bootstrap needed?
POST /api/v1/auth/change-password viewer+ Change your own password
GET /api/v1/status viewer+ Full library snapshot
GET /api/v1/events viewer+ Recent activity log
GET /api/v1/stream viewer+ Live event stream (Server-Sent Events)
GET /api/v1/volumes viewer+ List all volumes
GET /api/v1/outside viewer+ List outside-library tapes
DELETE /api/v1/outside/{barcode} operator+ Delete an outside-library tape
GET / POST /api/v1/cleaning/tapes viewer+ / operator+ List / create cleaning tapes
POST /api/v1/load operator+ Load a slot/ioslot into a drive
POST /api/v1/unload operator+ Unload a drive to a slot/ioslot
POST /api/v1/move operator+ Move between slot/ioslot elements
POST /api/v1/doors/io/{id}/open | /close operator+ Open/close an I/O (mailbox) door
POST /api/v1/doors/storage/{id}/open | /close operator+ Open/close a storage (magazine) door
POST /api/v1/drives/{index}/fault operator+ Inject/clear a simulated fault on one drive
POST /api/v1/robotics/fault operator+ Inject/clear a simulated fault on the shared robotic arm
POST /api/v1/volumes/{barcode}/write-protect operator+ Set/clear a volume's write-protect flag
GET /api/v1/offsite viewer+ List volumes in the offsite vault
POST /api/v1/offsite/send | /recall operator+ Send/recall a volume to/from the offsite vault
GET/POST/DELETE /api/v1/users admin Manage user accounts
GET/POST/DELETE /api/v1/tokens admin Manage scoped API tokens
GET/PUT /api/v1/settings admin View/update application settings
GET/PUT /api/v1/settings/latency admin View/update latency simulation settings
GET/PUT /api/v1/settings/cleaning admin View/update cleaning thresholds
GET/PUT /api/v1/settings/pin admin View/update the magazine/mailbox door PIN
GET/PUT /api/v1/settings/prometheus admin View/update whether the Prometheus exporter is enabled
GET /api/v1/prometheus/dashboard admin Download the pre-built Grafana dashboard JSON
GET/POST/PUT/DELETE /api/v1/logical-libraries[/{name}] admin Manage logical library partitions
GET /api/v1/unassigned admin Drives/slots/mailboxes in no logical library
GET/POST/PUT/DELETE /api/v1/drive-types[/{name}] admin Manage the drive-type catalog
GET/POST/PUT/DELETE /api/v1/drives[/{index}] admin Manage drives (hot-applies)
GET/POST/PUT/DELETE /api/v1/tape-types[/{name}] admin Manage the tape/media-type catalog
GET/POST/PUT/DELETE /api/v1/tape-sets[/{name}] admin Manage tape sets (type + storage folder)
GET /api/v1/fs/browse admin Browse server-side folders (tape-set storage picker)
GET/POST/PUT/DELETE /api/v1/magazines[/{id}] admin Manage magazines (hot-applies)
GET/POST/PUT/DELETE /api/v1/mailboxes[/{id}] admin Manage mailboxes (hot-applies)
GET /api/v1/backup/download admin Download an on-demand backup of state.db
GET/PUT /api/v1/backup/schedule admin View/update the recurring backup schedule
GET /api/v1/backups admin List stored (scheduled) backups
GET /api/v1/backups/{filename}/download admin Download a stored backup
DELETE /api/v1/backups/{filename} admin Delete a stored backup
POST /api/v1/restore admin Restore state.db from a backup file (restarts)
POST /api/v1/reset admin Factory reset (name-confirmed, restarts)
GET/POST /api/v1/wizard admin Current step / submit one wizard step
POST /api/v1/wizard/complete | /reset admin Finish, or reset progress of, the setup wizard
GET /api/v1/wizard/options admin Catalogs + current state for the wizard UI
GET /api/v1/kernel-mode/status viewer+ Whether gotochanger-kernel/the kernel module are available
GET /api/v1/kernel-mode/devices viewer+ Real device paths self-reported by running gotochanger-tcmud
GET /api/v1/snmp/mib viewer+ Download the dynamic MIB (see Monitoring)
GET /api/v1/openapi.json none OpenAPI 3.0 spec backing the Swagger UI at /docs

Load/Unload/Move (and GET /api/v1/status) additionally accept an X-Logical-Library: NAME header to scope the operation to one logical library.

A minimal curl example, using the trusted socket's HTTP-over-Unix-socket form:

curl --unix-socket /run/gotochanger/gotochanger.sock http://localhost/api/v1/status | jq .

Or against the TCP listener with a token:

curl -H "X-Api-Key: $GOTOCHANGER_TOKEN" http://localhost:8480/api/v1/status | jq .

For interactive exploration with full request/response schemas, use the Swagger UI at /docs (backed by /api/v1/openapi.json) - see the note in Monitoring about its current route coverage.

Migrate from disk-changer.in

You have an existing Bareos Storage Daemon using disk-changer.in as its Changer Command, with one or more Device = File resources pointed at plain files. gotochanger stays compatible with that same Device configuration, so the migration only touches the Autochanger's Changer Command line and how the archive device paths get populated - Bareos's own catalog (volume names, pools, retention) is untouched.

Prerequisites

Steps

  1. Note how many drives and how many total storage slots the existing setup uses - disk-changer.in's own config typically has this as a slot count and a directory of volume files.
  2. Run through the setup wizard, creating one magazine (call its ID mag1) sized to match your existing slot count (round up to the nearest multiple of 5), one drive per existing Device resource, and one tape set for your existing media. Skip mailboxes/logical libraries for now if you just want the fastest path back to a working Autochanger - you can add them later without disrupting Bareos.
  3. For each existing Device resource, edit only the Changer Command line:
    Changer Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V"
    
    Leave Archive Device, Media Type, Device Type = File, AutomaticMount, and RemovableMedia untouched.
  4. Register each existing volume as an outside-library tape under the tape set you just created, using its real existing barcode, then bring it straight into a storage slot through that magazine's storage door - this is the same open/queue/close pattern the dashboard's "Bulk load..." button uses:
    gotochangerctl tape-set add-tape <tape-set-name> <existing-barcode>
    gotochangerctl storage-door mag1 open
    gotochangerctl storage-door mag1 close '[{"action":"load","address":1,"barcode":"<existing-barcode>"}]'
    
    Repeat the last line (with the next free storage-slot address) for each existing volume.
  5. Restart bareos-sd, then verify Bareos still sees the expected slot/drive count:
    gotochanger-changer /dev/null slots
    gotochanger-changer /dev/null listall
    

Verify

bconsole <<'EOF'
status storage=FakeML3 slots
EOF

Expect the same total slot count Bareos reported before the migration, and gotochangerctl status to show the same barcodes racked in storage slots your old disk-changer.in setup had. Run a small backup/restore job against one volume to confirm read/write still works end-to-end before decommissioning the old script.

Partition one physical robot into two logical libraries

You have one gotochangerd instance with enough drives/magazines/mailboxes to serve two separate Bareos Storage Daemons, and want each SD to see its own independent Autochanger without either one being able to touch the other's media.

Prerequisites

Steps

  1. Check what's currently unassigned, to confirm the drive indices and magazine/mailbox IDs you'll use:
    gotochangerctl unassigned
    
  2. Create the two logical libraries, assigning drives/magazines/mailboxes directly at creation time (logical-library new <name> <drive-indices csv> <magazine-ids csv> <mailbox-ids csv>):
    gotochangerctl logical-library new Library1 0,1 mag1 mbx1
    gotochangerctl logical-library new Library2 2,3 mag2 mbx2
    
    Equivalently via the REST API:
    curl -X POST --unix-socket /run/gotochanger/gotochanger.sock \
      http://localhost/api/v1/logical-libraries \
      -H 'Content-Type: application/json' \
      -d '{"name":"Library1","drives":[0,1],"magazines":["mag1"],"mailboxes":["mbx1"]}'
    
  3. In the Admin web UI, open Logical Libraries > Library1 and click Bareos Config - this generates a ready-to-paste Autochanger/Device block with the correct Drive Index values already filled in for Library1's two drives. Repeat for Library2. If you'd rather build the block by hand, gotochangerctl logical-library show Library1 and gotochangerctl drive list return the same underlying data.
  4. Paste each generated block into the corresponding Bareos Storage Daemon's config, giving each Autochanger its own --logical-library suffix on the Changer Command line:
    Changer Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V --logical-library=Library1"
    
    Changer Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V --logical-library=Library2"
    
  5. Restart both Storage Daemons.

Verify

gotochangerctl --logical-library=Library1 status
gotochangerctl --logical-library=Library2 status

Each should show only its own drives/slots/ioslots, with dense addresses starting from 1 (0 for drives) within that scope - the physical addresses underneath are unaffected. Confirm cross-library isolation by attempting a move that crosses the boundary from Library1 into a slot that belongs to mag2:

gotochangerctl --logical-library=Library1 move slot 1 slot <a-mag2-slot-address>

Expect an error rejecting the move because the destination isn't in Library1 - this is exactly what keeps the two Bareos SDs from touching each other's media while sharing one physical robot.

Configure multiple drives without the Drive Index trap

You're adding a second (or third) drive to an existing Autochanger and want Bareos to actually address each one independently, instead of every Device silently reporting as "drive 0".

Prerequisites

Steps

  1. List gotochanger's own drives, to confirm their indices and device paths:
    gotochangerctl drive list
    
    Expected output (one line per drive):
    0    /var/lib/gotochanger/drives/drive0
    1    /var/lib/gotochanger/drives/drive1
    
  2. In the Bareos Autochanger's config, add an explicit Drive Index to every Device resource, set to that Device's 0-based position within this Autochanger's own Device = list - not gotochangerd's drive index (they only happen to match when both start at 0 and are contiguous, which is the common but not universal case):
    Autochanger {
      Name = FakeML3
      Device = Drive0, Drive1
      Changer Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V"
    }
    Device {
      Name = Drive0
      Drive Index = 0
      ...
    }
    Device {
      Name = Drive1
      Drive Index = 1
      ...
    }
    
  3. Restart bareos-sd.

Verify

gotochanger-changer /dev/null loaded 0
gotochanger-changer /dev/null loaded 1

Load a different volume into each drive and confirm each Device resource reports the correct one:

gotochangerctl load slot 1 0
gotochangerctl load slot 2 1
bconsole <<'EOF'
status storage=FakeML3
EOF

Expect Drive0/Drive1 to each show their own distinct volume, not both showing the same one. If both Devices report identical status regardless of which drive actually has the tape, Drive Index is missing (or identical) on one of them - that's the trap: it silently defaults to 0 for any Device that doesn't set it, so a missing second Drive Index makes Bareos treat both Devices as the same physical drive.

Scheduled offsite rotation

You want a chosen number of full volumes automatically vaulted offsite on a recurring schedule, simulating routine tape rotation without an operator manually clicking "Send to offsite" every day - plus the ability to trigger a send/recall by hand (e.g. from a cron job or an external rotation script) when needed.

Prerequisites

Steps

Enable and configure scheduled rotation

gotochangerctl settings set offsite_location=true offsite_rotation_count=2 offsite_rotation_interval=24h

gotochangerd re-reads offsite_rotation_interval/offsite_rotation_count from the live settings store every few seconds, so this takes effect without a restart. Once the interval elapses, up to offsite_rotation_count of the least-recently-created full volumes currently in storage slots are sent offsite automatically.

Trigger a rotation manually (e.g. from cron)

gotochangerctl offsite list
gotochangerctl offsite send slot 3

Equivalently via curl:

curl -X POST --unix-socket /run/gotochanger/gotochanger.sock \
  http://localhost/api/v1/offsite/send \
  -H 'Content-Type: application/json' \
  -d '{"from_kind":"slot","from_address":3}'

A typical cron entry driving a nightly manual rotation instead of (or in addition to) the built-in scheduler:

0 2 * * * gotochangerctl offsite send slot 3 >> /var/log/gotochanger-offsite.log 2>&1

Recall a volume

gotochangerctl offsite recall <barcode> slot 3

Verify

gotochangerctl offsite list
gotochangerctl events | grep -i offsite

Expect the sent volume to disappear from storage-slot listings (gotochangerctl status) and appear in gotochangerctl offsite list; the activity log (and an SNMP trap, if enabled - see Monitoring) records a MEDIA.OFFSITE-SEND.SUCCESS-style event for both the manual and scheduled paths. A scheduled rotation that finds a candidate volume already moved by something else in the gap is skipped with a logged error rather than treated as fatal - check the events log if fewer volumes were rotated than expected.

Bareos resilience testing: fault injection

You want to verify your Bareos configuration (retries, alerting, failover) actually reacts correctly when a drive or the robotic arm fails mid-job - without waiting for real hardware to break. gotochanger can inject either kind of failure on demand and clear it just as easily.

Prerequisites

Steps

Inject a drive fault

gotochangerctl fault 0 on

Equivalently:

curl -X POST --unix-socket /run/gotochanger/gotochanger.sock \
  http://localhost/api/v1/drives/0/fault \
  -H 'Content-Type: application/json' \
  -d '{"fault":true}'

Kick off (or let continue) a Bareos job using drive 0. Expect Bareos to report a mount/drive error - exactly as if a real drive had failed - and your alerting to fire on it.

Clear it once you've confirmed the failure handling:

gotochangerctl fault 0 off

Inject a robotic-arm fault

There's only one shared robotic arm, so this affects every drive/logical library at once - useful for testing a full "changer offline" scenario:

gotochangerctl robotic-fault on blocked_arm "simulated jam for DR test"

Valid <kind> values: blocked_arm, mispositioned_cartridge, pickup_failure, drop_failure, movement_jam, other. Equivalently:

curl -X POST --unix-socket /run/gotochanger/gotochanger.sock \
  http://localhost/api/v1/robotics/fault \
  -H 'Content-Type: application/json' \
  -d '{"active":true,"kind":"blocked_arm","message":"simulated jam for DR test"}'

Every Load/Unload/Move now fails library-wide (door open/close still works) until cleared:

gotochangerctl robotic-fault off

Verify

gotochangerctl events

Expect DRIVE.FAULT.SET.SUCCESS and ROBOTICS.FAULT.SET.SUCCESS-style events (and, if SNMP is enabled, a matching trap for each - see Monitoring) around the times you injected each fault, followed by *.CLEAR.SUCCESS events once cleared. On the dashboard, the affected drive (or the Robotic Arm panel) shows the amber pulsing fault light for the whole window - see Drive Indicator Lights for the full light legend. Cross-check against Bareos's own job log/bconsole status storage output to confirm it actually surfaced the failure to an operator instead of silently retrying forever.

Switch to kernel mode for a third-party SCSI tool

A monitoring agent, a test harness, or some other tool in your stack insists on talking to a real SCSI medium changer (/dev/sg*) and tape drives (/dev/nst*) rather than a changer-script convention. Bareos itself never needs this - only enable kernel mode if something else genuinely requires a real kernel device node.

Prerequisites

Steps

  1. Switch operational mode to kernel:
    gotochangerctl settings set operational_mode=kernel
    
  2. gotochangerd's reconciler automatically starts one gotochanger-tcmud@<logical-library-name>.service instance per logical library (gotochanger-tcmud@default.service if the library is unscoped), via polkit-authorized systemd calls - no manual systemctl enable step is required. Confirm it's running:
    systemctl status 'gotochanger-tcmud@*.service'
    
  3. Check which real devices came up:
    curl --unix-socket /run/gotochanger/gotochanger.sock http://localhost/api/v1/kernel-mode/status | jq .
    curl --unix-socket /run/gotochanger/gotochanger.sock http://localhost/api/v1/kernel-mode/devices | jq .
    
    The Admin UI's Drives page and each logical library's "Kernel Mode Setup" dialog show the same device paths, plus the equivalent manual systemctl enable --now gotochanger-tcmud@<instance> command if you'd rather manage the instance yourself instead of relying on the automatic reconciler.
  4. Find the stable device symlinks - prefer these over raw /dev/sgN//dev/nstN numbers, which are not stable across a gotochanger-tcmud restart:
    ls -l /dev/tape/by-id/
    
  5. Point your third-party tool's device configuration at the scsi-<NAA> (changer) or scsi-<NAA>-nst (drive) symlink instead of the raw device number.

Verify

sg_inq /dev/tape/by-id/scsi-<NAA>
mt -f /dev/tape/by-id/scsi-<NAA>-nst status

Expect a real SCSI INQUIRY response and tape-drive status output, backed by the same plain files gotochanger already manages - loading a volume through gotochangerctl load/the dashboard should make it visible at that same device path within a few seconds. If the third-party tool reports the device is gone after a gotochanger-tcmud restart, check /dev/tape/by-id/ again rather than a previously-noted /dev/sgN number - that's exactly the instability the by-id symlinks exist to avoid.

Backup and restore for disaster recovery

The host running gotochangerd was rebuilt (or its disk was lost) and you need to restore topology, volume state, and settings from a previously downloaded backup - onto either the same host or a freshly installed one.

Prerequisites

A backup is a full snapshot of the whole database, not just topology. It contains every user account and API token's credential hash alongside slots/drives/tape sets/settings. Restoring replaces the entire database, including users and tokens - the temporary Admin password you just bootstrapped on the new install will stop working the moment the restore completes, replaced by whatever accounts existed at backup time.

Taking a backup (before disaster strikes)

gotochangerctl backup download ./gotochanger-backup.db

Or schedule recurring backups so you always have a recent one:

gotochangerctl backup schedule set interval=24h retention=7

Restoring onto the rebuilt host

  1. Install gotochanger fresh (see Installation) and bootstrap a temporary Admin password - this account only exists long enough to perform the restore.
  2. Restore the backup:
    gotochangerctl restore ./gotochanger-backup.db
    
    Equivalently via curl:
    curl -X POST --unix-socket /run/gotochanger/gotochanger.sock \
      http://localhost/api/v1/restore \
      --data-binary @./gotochanger-backup.db
    
  3. The daemon validates the file (SQLite header + expected schema), atomically swaps it in, and restarts. Expect a brief unreachable window.
  4. Sign in again - using the credentials that existed at backup time, not the temporary bootstrap password from step 1.

Verify

gotochangerctl status
gotochangerctl user list
gotochangerctl volumes

Expect the same topology (magazines, mailboxes, drives, logical libraries), the same tape sets/volumes, and the same user accounts/roles that existed when the backup was taken - not the temporary bootstrap account from step 1, which no longer exists post-restore. Re-issue any API tokens your automation depends on if you aren't certain they survived in the backup, since a token's raw value is never recoverable - only its hash is stored, matching what was true before the disaster.

Monitor gotochanger via SNMP

You want an existing NMS (Nagios, Zabbix, PRTG, or similar) to receive SNMPv2c traps for library events - robotic/drive faults, media movement, authentication, configuration changes - and to decode them using gotochanger's own MIB.

Prerequisites

Steps

  1. Enable SNMP and point it at your receiver:
    gotochangerctl settings set snmp_enabled=true snmp_enterprise_oid=1.3.6.1.4.1.55555.1
    
    Targets (host:port:community, one or more) aren't expressible via the CLI's simple key=value form - set them from Admin > Settings > "SNMP traps", or directly:
    curl -X PUT --unix-socket /run/gotochanger/gotochanger.sock \
      http://localhost/api/v1/settings \
      -H 'Content-Type: application/json' \
      -d '{"snmp_targets":[{"host":"nms.example.com","port":162,"community":"public"}]}'
    
  2. Download the MIB matching your configured enterprise OID:
    curl --unix-socket /run/gotochanger/gotochanger.sock \
      http://localhost/api/v1/snmp/mib -o gotochanger.mib
    
    (Or click the same link from Admin > Settings > SNMP traps in the web UI.)
  3. Load gotochanger.mib into your NMS's MIB browser/trap decoder.
  4. Trigger an event to confirm delivery - a drive fault is the simplest:
    gotochangerctl fault 0 on
    gotochangerctl fault 0 off
    

Verify

On your NMS, expect one decoded trap for the fault-set event and one for the fault-clear event, each carrying the sysUpTime, snmpTrapOID, a human-readable message, and structured code/category/severity/ outcome/operation varbinds plus a key=value; ... detail string - not just an opaque numeric OID. If the trap arrives but decodes as an unknown OID, re-download the MIB: it's rendered against your current snmp.enterprise_oid, so an MIB downloaded before changing that setting will no longer match. Cross-check against gotochangerctl events, which shows the exact same event that produced the trap, to confirm nothing was dropped in transit.

Managing cleaning tapes

You want gotochanger to simulate drive cleaning realistically: cartridges that wear out after a fixed number of cleaning cycles, and a mount-count threshold that decides when a drive actually needs cleaning.

Prerequisites

Steps

Configure thresholds

gotochangerctl cleaning settings get
gotochangerctl cleaning settings set enabled=true mode=backup_robot max_uses=20 mount_threshold=50 duration=2m

Two modes are available:

Create a cleaning cartridge

gotochangerctl cleaning tape new
gotochangerctl cleaning tape list

Up to 5 cleaning cartridges can exist at once, barcoded with a fixed NNNNNCLN format (e.g. 00001CLN) - not admin-configurable, unlike regular tape sets.

Run a cleaning cycle manually (backup_software mode)

Loading a cleaning cartridge into a drive like any other volume is enough - gotochangerd detects it's a cleaning tape and branches into the cleaning path automatically:

gotochangerctl load slot <cleaning-tape-slot> 0

Verify

gotochangerctl cleaning tape list
gotochangerctl events | grep -i cleaning

Expect CLEANING.CYCLE-START.SUCCESS then CLEANING.CYCLE-SUCCESS events around the load, the drive's MountsSinceCleaning counter reset to 0 for that drive, and the cartridge's own usage count incremented by one. Once a cartridge's usage count reaches max_uses, its state moves to expired and it can no longer be auto-selected or manually loaded (gotochangerctl load returns an error) - replace it:

gotochangerctl cleaning tape new

and, if the expired cartridge was one of 5 already at the pool limit, note that a CLEANING.TAPE-CREATE.FAILURE event (pool full) means you need to physically account for/remove the expired one from its magazine before creating a replacement.

Set up a scoped operator API token for a CI script

An external CI job needs to drive day-to-day operations (load/unload/move, inject a fault for a resilience test, read status) against gotochangerd, but should never be able to touch Users, Tokens, Settings, or Backup

Prerequisites

Steps

  1. Create a scoped token - the raw value is only ever shown once, right here:
    gotochangerctl token new ci-pipeline operator
    
    Equivalently via curl:
    curl -X POST --unix-socket /run/gotochanger/gotochanger.sock \
      http://localhost/api/v1/tokens \
      -H 'Content-Type: application/json' \
      -d '{"name":"ci-pipeline","role":"operator"}'
    
  2. Store the returned value in your CI system's secret store (e.g. a GitHub Actions repository secret, GOTOCHANGER_TOKEN) - it will never be displayed again; only its SHA-256 hash is kept server-side.
  3. Use it from the CI script against the TCP listener (the trusted Unix socket isn't reachable from outside the host, and always behaves as Admin anyway, which defeats the point of scoping):
    curl -H "X-Api-Key: $GOTOCHANGER_TOKEN" http://gotochanger-host:8480/api/v1/status
    
    or with gotochangerctl --url http://gotochanger-host:8480 --token "$GOTOCHANGER_TOKEN" status.

Verify

gotochangerctl token list

Expect ci-pipeline listed with role operator. Confirm the scope is actually enforced - an Admin-only route should be rejected:

curl -s -o /dev/null -w '%{http_code}\n' -H "X-Api-Key: $GOTOCHANGER_TOKEN" \
  http://gotochanger-host:8480/api/v1/users

Expect 403, not 200 - proving the token can't manage users/tokens/settings/backups even though it can freely load/unload/move volumes and inject faults. When the CI script is decommissioned, revoke it rather than leaving it live:

gotochangerctl token revoke ci-pipeline

Monitor gotochanger via Prometheus and Grafana

You want an existing Prometheus server to scrape library metrics (slot/reader/volume counts, operation throughput and latency, error rates) and a Grafana dashboard to visualize them.

Prerequisites

Steps

  1. Enable the exporter:
    gotochangerctl prometheus enable
    
    (Or from the web UI: Admin > Settings > "Prometheus" > enable the checkbox > Save.)
  2. Confirm it's serving real values - no credentials needed:
    curl http://localhost:8480/metrics
    
  3. Point your Prometheus server at it:
    scrape_configs:
      - job_name: 'gotochanger'
        static_configs:
          - targets: ['<gotochangerd-host>:8480']
    
    Reload/restart Prometheus to pick up the new scrape config.
  4. Download the pre-built Grafana dashboard:
    gotochangerctl prometheus dashboard gotochanger-dashboard.json
    
    (Or click "Download Grafana dashboard" from the same Admin > Settings > Prometheus panel.)
  5. In Grafana: Dashboards > New > Import > upload gotochanger-dashboard.json > when prompted, select the Prometheus data source scraping this gotochangerd instance.
  6. Generate some activity to see the dashboard populate - a load/unload cycle is the simplest:
    gotochangerctl load slot 1 0
    gotochangerctl unload 0 slot 1
    

Verify

The imported dashboard's Overview row should show your real slot/reader/volume counts within one Prometheus scrape interval. After the load/unload cycle in step 6, the Operations Timeline row's rate graph should show a load and an unload sample, and the latency panel a non-zero p95 for both. If a panel shows "No data," double-check the data source selected during import matches the one actually scraping this instance - the dashboard's queries assume metric names exactly as gotochangerd exports them (gotochanger_*), so a differently-labeled or relabeled scrape job will need its queries adjusted.

Metrics reflect activity from any client - the web UI, gotochangerctl/gotochanger-changer over the trusted socket, or direct API calls - so a real Bareos backup job driving this daemon shows up here exactly like the manual load/unload above did.

Troubleshooting and FAQ

I forgot the Admin password.

There's no in-app recovery flow for the built-in account by design (sessions and credentials are kept deliberately simple). Whoever manages the host running gotochangerd will need to create a new Admin user via gotochangerctl user new <name> admin <password> against the trusted local socket, which bypasses web login entirely.

Why don't I see an Admin button?

You're signed in as a Viewer. Viewer accounts see the dashboard and read-only Admin screens are hidden entirely, not just disabled - ask an Admin to change your role if you need Operator or Admin access (gotochangerctl user role <username> operator).

A Load/Unload/Move button (or command) gave me an error about "no destination available".

That means every eligible destination (an empty drive, slot, or I/O slot, depending on the action) is currently occupied. Free one up - unload a drive, move a tape out of a slot - and try again.

Everything is rejected with a 409 / "robotic arm is in fault state".

The robotic arm has an active simulated fault. Clear it (gotochangerctl robotic-fault off, or the Robotic Arm panel's Clear fault button) - door open/close still work while a fault is active, but Load/Unload/ Move are rejected library-wide until it's cleared. See Bareos resilience testing if you triggered this deliberately.

My cartridge got a barcode I didn't expect.

Barcodes come entirely from the tape set's tape type format - check Admin > Tape Types for the tape set's type to see its exact family/media-id/length, or Admin > Tape Sets to confirm which tape type the set actually uses.

Operations feel instantaneous - is latency simulation on?

Check Admin > Latency > Enable. It's off by default on a fresh install; turning it on (with the "Load defaults" prefill, or your own tuned values) is also what makes the drive's "active operation" light visibly blink for more than an instant - see Drive Indicator Lights.

Does gotochanger create a real /dev/sg* or /dev/nst* device?

Only if you've explicitly enabled Kernel Mode (the separate gotochanger-kernel package, operational_mode=kernel). By default gotochanger runs in userspace/file mode, where a loaded drive is a plain symlink at a configured device path and Bareos never needs a kernel SCSI device for this integration - it just calls a Changer Command script. Kernel mode exists only for third-party tools that insist on a real device node; see Switch to kernel mode.

A magazine/mailbox door won't open - "PIN required" or "invalid PIN".

That magazine or mailbox has a 4-digit PIN configured (Admin > Settings > PIN). Pass it as the optional pin argument: gotochangerctl storage-door <id> open <pin> / gotochangerctl io-door <id> open <pin>. An empty PIN in Admin > Settings clears the requirement entirely.

The Swagger UI at /docs doesn't show every endpoint this guide documents.

Correct, and known - the static OpenAPI spec currently documents the most commonly used routes but hasn't been fully expanded to cover every admin/topology endpoint yet. Treat CLI Reference and REST API as the authoritative list until that's addressed; every route listed there works today even if Swagger doesn't render it yet.

Where do I configure Bareos itself to talk to gotochanger?

See Bareos Integration - Autochanger/Device resources, the Drive Index gotcha, and scoping one Autochanger to one logical library - or generate a ready-to-paste config skeleton from Admin > Logical Libraries > "Bareos Config".