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.
/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:
- Viewer — read-only. Can see the dashboard and Admin screens but can't load/unload tapes, open doors, or change anything.
- Operator — everything a Viewer can do, plus day-to-day operation: load/unload/move tapes, open and close doors, raise/clear drive and robotic-arm faults, download a manual backup.
- Admin — everything, including all of Admin > Library Topology, user/token management, scheduled backups, and restore.
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.
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.
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).
- Operational Mode - name your virtual tape library (the VTL name shown throughout the UI) and pick the
operational mode:
changer(default userspace/file mode) orkernel(see Kernel Mode). - Drives - choose how many physical drives to create and which drive type each one is, from the drive-type catalog.
- Magazines - define one or more magazines (storage slot groups, 5-20 slots each).
- 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.
- 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.
- 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.
- 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.
- 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.
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.
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
- Install the separate
gotochanger-kernelpackage (depends ongotochangerandpolkitd). It needs root and thetarget_core_userkernel module - the package's postinst does a best-effortmodprobe. - 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 onegotochanger-tcmud@<logical-library-name>.serviceinstance per logical library (@defaultif the library is unscoped) via polkit-authorized systemd calls - no manualsystemctl enablestep is required, though it's supported (the Admin UI's per-library "Kernel Mode Setup" dialog shows the equivalent manualsystemctl enable --now gotochanger-tcmud@<instance>command for cases where automatic management isn't wanted). - 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/nstNnumbers, which are not stable across agotochanger-tcmudrestart. Admin > Drives and the Bareos-Config generator button both show the actual current device paths. - 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:
- Drives: set a Drive Type's SCSI Identity to Realistic - Admin > Drive Types' New/Edit dialog,
gotochangerctl drive-type new|update ... --scsi-identity realistic, orscsi_identity: realisticvia the API. Only defined forLTO-8/LTO-9generations today (reports as a realIBM ULT3580-TD8/-TD9)- any other generation stays on the default identity even with this set.
- Changer: set a logical library's Changer Model to Realistic - Admin > Logical Libraries'
New/Edit dialog,
gotochangerctl logical-library new|update ... --changer-model realistic, orchanger_model: realisticvia the API. Reports as anSTK SL150- the same real device this project's own SMC-3 command layout was verified against (see the Oracle StorageTek SL150 SCSI Reference Guide, cited throughoutinternal/scsi). Only takes effect for agotochanger-tcmudinstance scoped to that logical library (--logical-library); an unscoped instance has no logical library to read this setting from and always reports the default identity.
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
- Drive bandwidth throttling only applies in kernel mode. In the default userspace/file mode, a loaded
drive is a symlink at the configured device path; the consuming application writes directly to that file
and gotochangerd never sees the byte stream, so there's nothing to throttle. In kernel mode,
gotochanger-tcmudsits directly in the SCSI I/O path and does throttle reads/writes to the assigned drive type's configured native speed. - Backstore/WWN names are prefixed with the instance name (the logical library name, or
default) to avoid kernel-level name collisions between concurrentgotochanger-tcmudinstances. gotochanger-tcmudrequires root and is not started or enabled automatically just by installing the package - only once operational mode is actually set tokernel.
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.
- Drive Types - the catalog of drive hardware models offered during setup and used by Logical Libraries.
- Tape Types - the catalog of media families (LTO/DLT/SDLT/DDS/AIT/3592/generic), each defining its own
barcode format (see the barcode reference table in
Overview and Concepts - unless noted otherwise in that table,
New tape typechooses the format for you). - Tape Sets - groups of cartridges by tape type, each stored under its own folder on disk. New tape set creates the set and its first batch of cartridges in one step; Add tapes on an existing set tops it up later (auto-generated or a manually-typed barcode).
- Drives - the physical drive device list. Removing anything other than the highest-indexed drive shifts
every later drive's index, so double-check which drive you mean to remove if there's more than one - this
is exactly the kind of shift that produces the Drive Index trap on the Bareos side
if the corresponding
Deviceresources aren't updated to match. - Magazines / Mailboxes - storage-slot and I/O-slot groups, respectively. Either can optionally require a 4-digit PIN to open its door (Admin > Settings > PIN) - an empty PIN clears the requirement.
- Logical Libraries - create/edit partitions of drives, magazines, and mailboxes; the Unassigned list at
the bottom shows anything not yet claimed by one. The Bareos Config button on each logical library
generates a ready-to-paste Autochanger/Device block, including the correct
Drive Indexper drive.
Operations
- Latency - seven independently-tunable simulated delays (drive load/unload, tape positioning, robotic
arm movement for tape moves vs. magazine scans, post-close magazine scanning, door open/close) applied
library-wide, editable live with a "Load defaults" prefill (
gotochangerctl latency get|set|reset). Turning this on is what makes the busy indicator light actually visible for more than an instant. - Cleaning tapes - thresholds (mount count before a drive needs cleaning), maximum uses per cartridge, and
cleaning duration are all configured here (
gotochangerctl cleaning settings get|set|reset). See Managing cleaning tapes for the full lifecycle and a worked example. - Settings - default volume capacity, offsite rotation schedule, daemon-level knobs (log level, capacity poll interval), and SNMP trap configuration - see Monitoring.
- Backup - a backup is a full snapshot of the database (
VACUUM INTO): topology, every setting above, current slot/drive/volume state, and - since user accounts and API tokens live in that same database - the stored credential hashes too. Because a backup file therefore carries password hashes, every action here is Admin-only: taking and downloading a manual backup, scheduling recurring backups (interval + retention), browsing previously stored ones, and restoring. Restoring replaces the entire database (atomically, then restarts the service) and genuinely overwrites user accounts and tokens along with topology - see Backup and restore for disaster recovery for the full procedure and what to check afterward. - Factory reset -
gotochangerctl reset <confirm-name>(or the Admin UI equivalent) wipes the database back to empty defaults: no topology, no users/tokens (bootstrap required again), wizard not completed - exactly like a fresh install. It requires typing the VTL's current name as confirmation, and takes a safety-net backup automatically before wiping anything. Optional--delete-volumesalso deletes every cartridge's backing file on disk. Like restore, this restarts the service.
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:
ROBOTICS.LOAD.SUCCESSROBOTICS.MOVE.FAILUREDRIVE.FAULT.SET.SUCCESSAUTH.LOGIN.FAILURECONFIG.SETTINGS.UPDATE.SUCCESS
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:
-
Web UI: Admin > Settings > "SNMP traps" panel (Enabled, Enterprise OID, Agent address, and Targets - one per line,
host:port:community). -
CLI, for the scalar fields:
gotochangerctl settings set snmp_enabled=true snmp_enterprise_oid=1.3.6.1.4.1.55555.1Targets are a list, which the CLI's simple
key=valueform can't express - set them via the web UI, or with a directPUT /api/v1/settingscall (snmp_targets, an array of{host, port, community}).
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.
/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
- The existing
disk-changer.in-based Autochanger's Device count and currentArchive Devicepaths (e.g./etc/bareos/scripts/disk-changer.confor the Device resources themselves). - gotochanger installed (see Installation) but not yet configured (fresh wizard state).
bareosadded to thegotochangergroup so it can reach the trusted socket and the volume files:sudo adduser bareos gotochanger.
Steps
- 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. - 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. - For each existing Device resource, edit only the
Changer Commandline:
LeaveChanger Command = "/usr/bin/gotochanger-changer %c %o %S %a %d %V"Archive Device,Media Type,Device Type = File,AutomaticMount, andRemovableMediauntouched. - 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:
Repeat the last line (with the next free storage-slot address) for each existing volume.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>"}]' - 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
- At least 4 drives and 2 magazines (
mag1,mag2) and 2 mailboxes (mbx1,mbx2) already configured - one magazine/mailbox pair and two drives per logical library, minimum. - Admin access to gotochangerd.
Steps
- Check what's currently unassigned, to confirm the drive indices and magazine/mailbox IDs you'll use:
gotochangerctl unassigned - 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>):
Equivalently via the REST API:gotochangerctl logical-library new Library1 0,1 mag1 mbx1 gotochangerctl logical-library new Library2 2,3 mag2 mbx2curl -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"]}' - 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 Indexvalues already filled in for Library1's two drives. Repeat for Library2. If you'd rather build the block by hand,gotochangerctl logical-library show Library1andgotochangerctl drive listreturn the same underlying data. - Paste each generated block into the corresponding Bareos Storage Daemon's config, giving each Autochanger
its own
--logical-librarysuffix on theChanger Commandline: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" - 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
- An Autochanger resource with 2+ Device resources already defined in
bareos-sd.d/. - 2+ drives already created in gotochanger (
gotochangerctl drive list).
Steps
- List gotochanger's own drives, to confirm their indices and device paths:
Expected output (one line per drive):gotochangerctl drive list0 /var/lib/gotochanger/drives/drive0 1 /var/lib/gotochanger/drives/drive1 - In the Bareos Autochanger's config, add an explicit
Drive Indexto every Device resource, set to that Device's 0-based position within this Autochanger's ownDevice =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 ... } - 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
- Offsite vaulting enabled for this library (set during the setup wizard, or via Admin > Settings).
- At least one full storage-slot volume to rotate.
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
- A running backup job (or one ready to run) against a gotochanger-backed Autochanger.
- Operator access (drive/robotic faults are an Operator-level action, not Admin-only).
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
- The
gotochanger-kernelpackage installed (sudo apt-get install gotochanger-kernel). - Root access on the host (kernel mode needs the
target_core_userkernel module and configfs). - An existing logical library (or the whole physical library, unscoped) you want to expose.
Steps
- Switch operational mode to
kernel:gotochangerctl settings set operational_mode=kernel - gotochangerd's reconciler automatically starts one
gotochanger-tcmud@<logical-library-name>.serviceinstance per logical library (gotochanger-tcmud@default.serviceif the library is unscoped), via polkit-authorized systemd calls - no manualsystemctl enablestep is required. Confirm it's running:systemctl status 'gotochanger-tcmud@*.service' - Check which real devices came up:
The Admin UI's Drives page and each logical library's "Kernel Mode Setup" dialog show the same device paths, plus the equivalent manualcurl --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 .systemctl enable --now gotochanger-tcmud@<instance>command if you'd rather manage the instance yourself instead of relying on the automatic reconciler. - Find the stable device symlinks - prefer these over raw
/dev/sgN//dev/nstNnumbers, which are not stable across agotochanger-tcmudrestart:ls -l /dev/tape/by-id/ - Point your third-party tool's device configuration at the
scsi-<NAA>(changer) orscsi-<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 previously downloaded backup file (see "Taking a backup" below if you don't have one yet).
- Admin access to the new gotochangerd instance (a fresh install, bootstrapped with a temporary Admin password).
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
- Install gotochanger fresh (see Installation) and bootstrap a temporary Admin password - this account only exists long enough to perform the restore.
- Restore the backup:
Equivalently via curl:gotochangerctl restore ./gotochanger-backup.dbcurl -X POST --unix-socket /run/gotochanger/gotochanger.sock \ http://localhost/api/v1/restore \ --data-binary @./gotochanger-backup.db - The daemon validates the file (SQLite header + expected schema), atomically swaps it in, and restarts. Expect a brief unreachable window.
- 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
- An SNMP trap receiver reachable from the gotochangerd host.
- Admin access to gotochangerd.
Steps
- Enable SNMP and point it at your receiver:
Targets (gotochangerctl settings set snmp_enabled=true snmp_enterprise_oid=1.3.6.1.4.1.55555.1host:port:community, one or more) aren't expressible via the CLI's simplekey=valueform - 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"}]}' - Download the MIB matching your configured enterprise OID:
(Or click the same link from Admin > Settings > SNMP traps in the web UI.)curl --unix-socket /run/gotochanger/gotochanger.sock \ http://localhost/api/v1/snmp/mib -o gotochanger.mib - Load
gotochanger.mibinto your NMS's MIB browser/trap decoder. - 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
- Admin access to gotochangerd.
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:
backup_software- cleaning cartridges sit in a magazine assigned to a logical library, so your backup software (e.g. Bareos) decides when to mount/unmount them; gotochangerd only tracks usage/expiry.backup_robot- cartridges sit in a magazine not assigned to any logical library (invisible to Bareos); gotochangerd's own background sweep finds idle drives at/overmount_thresholdmounts-since-last-cleaning and runs the cycle itself, auto-ejecting back to origin when done.
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
- exactly what the
operatorrole is for.
Prerequisites
- Admin access to gotochangerd.
- The CI system's secret-storage mechanism, to hold the token once issued.
Steps
- Create a scoped token - the raw value is only ever shown once, right here:
Equivalently via curl:gotochangerctl token new ci-pipeline operatorcurl -X POST --unix-socket /run/gotochanger/gotochanger.sock \ http://localhost/api/v1/tokens \ -H 'Content-Type: application/json' \ -d '{"name":"ci-pipeline","role":"operator"}' - 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. - 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):
or withcurl -H "X-Api-Key: $GOTOCHANGER_TOKEN" http://gotochanger-host:8480/api/v1/statusgotochangerctl --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
- A Prometheus server that can reach the gotochangerd host's HTTP listener.
- A Grafana instance with a Prometheus data source configured (or you'll add one during import).
- Admin access to gotochangerd.
Steps
- Enable the exporter:
(Or from the web UI: Admin > Settings > "Prometheus" > enable the checkbox > Save.)gotochangerctl prometheus enable - Confirm it's serving real values - no credentials needed:
curl http://localhost:8480/metrics - Point your Prometheus server at it:
Reload/restart Prometheus to pick up the new scrape config.scrape_configs: - job_name: 'gotochanger' static_configs: - targets: ['<gotochangerd-host>:8480'] - Download the pre-built Grafana dashboard:
(Or click "Download Grafana dashboard" from the same Admin > Settings > Prometheus panel.)gotochangerctl prometheus dashboard gotochanger-dashboard.json - In Grafana: Dashboards > New > Import > upload
gotochanger-dashboard.json> when prompted, select the Prometheus data source scraping this gotochangerd instance. - 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".