restic — Structure#
Layout pattern#
Standard Go Layout (cmd/internal) — No Public Library Surface
restic uses the standard cmd/ + internal/ split but deliberately omits any pkg/ directory. Every package is under internal/, making it clear that restic is a tool, not a library. The single binary is produced from cmd/restic. This is a “flat monolith” in terms of binaries, but internally well-layered.
Directory map#
restic/
├── cmd/
│ └── restic/ # Single binary: CLI commands (one file per command, 83 .go files)
│ └── testdata/ # Integration test fixtures
├── internal/
│ ├── restic/ # Domain core: ID, Blob, Snapshot, Node, Pack, Index types (29 files)
│ ├── backend/ # Backend interface + 14 storage driver implementations
│ │ ├── all/ # Registers all backends for main.go wiring
│ │ ├── azure/ # Azure Blob Storage
│ │ ├── b2/ # Backblaze B2
│ │ ├── cache/ # Local caching layer over any backend
│ │ ├── dryrun/ # Dry-run backend (no writes)
│ │ ├── gs/ # Google Cloud Storage
│ │ ├── layout/ # Filesystem layout strategies
│ │ ├── limiter/ # Bandwidth limiter wrapper
│ │ ├── local/ # Local filesystem backend
│ │ ├── location/ # URL-based backend resolution
│ │ ├── logger/ # Logging wrapper backend
│ │ ├── mem/ # In-memory backend (for testing)
│ │ ├── mock/ # Mock backend (for testing)
│ │ ├── rclone/ # rclone subprocess backend
│ │ ├── rest/ # REST server backend
│ │ ├── retry/ # Retry wrapper backend
│ │ ├── s3/ # AWS S3 / MinIO
│ │ ├── sema/ # Semaphore (concurrency limiter) wrapper
│ │ ├── sftp/ # SFTP backend
│ │ ├── swift/ # OpenStack Swift
│ │ ├── test/ # Shared backend acceptance test suite
│ │ └── util/ # Shared backend utilities
│ ├── repository/ # Repository operations: packing, indexing, encryption (29 files)
│ │ ├── hashing/ # Hashing writer
│ │ ├── index/ # In-memory index management (12 files)
│ │ └── pack/ # Pack file reading
│ ├── archiver/ # Backup/snapshot creation (18 files)
│ ├── restorer/ # Snapshot restore (18 files)
│ ├── fs/ # Filesystem abstraction (OS, virtual, node; 62 files)
│ ├── crypto/ # AES-256-CTR + Poly1305-AES encryption primitives (7 files)
│ ├── checker/ # Repository integrity checking
│ ├── data/ # Raw data blob storage helpers (22 files)
│ ├── filter/ # File inclusion/exclusion pattern matching (8 files)
│ ├── fuse/ # FUSE read-only mount of repository snapshots (12 files)
│ ├── dump/ # Dump snapshot contents to stdout (tar/zip; 8 files)
│ ├── walker/ # Tree walker over repository snapshots
│ ├── migrations/ # Repository format migrations
│ ├── feature/ # Feature flags system
│ ├── options/ # Extended options parsing
│ ├── global/ # Global flags, context setup, version
│ ├── errors/ # Error helpers and sentinel errors
│ ├── debug/ # Debug logging (build-tag gated)
│ ├── bloblru/ # LRU cache for blobs
│ ├── selfupdate/ # Self-update command implementation
│ ├── terminal/ # Low-level terminal output (19 files)
│ ├── textfile/ # Text file reading with encoding detection
│ └── ui/ # User-facing progress/status reporting
│ ├── backup/ # Backup progress UI (8 files)
│ ├── restore/ # Restore progress UI (6 files)
│ ├── progress/ # Generic progress counter
│ ├── table/ # Table formatting
│ ├── signals/ # OS signal handling
│ └── termstatus/ # Terminal status line management
├── changelog/ # Per-version changelog entries (structured text)
├── contrib/ # Community scripts and integrations (not Go)
├── doc/ # Sphinx-based documentation + man pages
├── docker/ # Dockerfile + entrypoint + build scripts
├── helpers/ # Release helper scripts (shell)
├── build.go # Custom build script (invoked via `go run build.go`)
├── Makefile # Thin wrapper around build.go
├── go.mod / go.sum # Module definition
└── VERSION # Version string file (read by build.go)Entry points#
Single binary:
cmd/restic/main.go→ produces theresticbinary- Initialises global options and terminal status
- Wires all backends via
internal/backend/all - Registers ~25 top-level cobra commands (backup, restore, check, prune, forget, snapshots, ls, find, diff, copy, mount, key, tag, migrate, repair, init, etc.)
- Commands that are platform-conditional (
mount,self-update,debug) use build-tag-gated_disabled.gostubs
Package organization#
Internal packages (all under
internal/):internal/restic— domain core: canonical types (ID,Blob,Snapshot,Node,Pack,Index,FileType,Backendinterface). The dependency anchor — everything imports this; it imports nothing internal.internal/backend—Backendinterface + 14 pluggable storage drivers as subdirectories; decorator backends (cache, retry, limiter, sema, logger, dryrun) wrap anyBackendimplementationinternal/repository— higher-level repository operations: packing blobs, managing the index, encryption at rest, locking; importsinternal/resticandinternal/backendinternal/archiver— snapshot creation: filesystem traversal, chunking, deduplication, pack uploadinginternal/restorer— snapshot restoration: parallel file writing, permission restorationinternal/fs— filesystem abstraction (fs.FS, virtual filesystems, node types for different OS)internal/crypto— AES-256-CTR + Poly1305-AES encryption, key derivationinternal/checker— repository consistency checks (orphaned blobs, pack integrity, etc.)internal/fuse— FUSE filesystem presenting snapshots as a virtual directory treeinternal/filter— glob-based file inclusion/exclusion pattern matchinginternal/feature— compile-time and runtime feature flag support (RESTIC_FEATURESenv var)internal/ui/*— layered UI:termstatus(raw terminal),terminal(output routing),backup/restore(domain-specific progress),progress,table,signalsinternal/global— global flags struct (Options), context creation, version string, profilinginternal/debug— build-tag-gated debug logging (debugtag enables it)internal/errors—errors.IsFatal(),errors.Wrap(), sentinel error helpersinternal/migrations— repository format migration handlersinternal/options— extended key=value options parsing for backend configurationinternal/bloblru— LRU cache for frequently accessed blobsinternal/selfupdate— binary self-update from GitHub releasesinternal/dump— dump snapshot content to stdout (tar or zip)internal/walker— tree traversal over repository snapshotsinternal/data— raw data access helpers
Public packages (pkg/): None — restic has no public Go library surface.
Layering:
cmd/restic (CLI glue, cobra wiring) ↓ internal/global, internal/ui/* (cross-cutting: flags, output) ↓ internal/archiver, internal/restorer, internal/checker, internal/fuse ↓ internal/repository (repository abstraction: encryption, packing, indexing) ↓ internal/backend/* (storage drivers, decorator wrappers) ↓ internal/restic (domain types — dependency inversion anchor) ↓ internal/crypto, internal/fs, internal/filter (leaf utilities)This is a clean layered architecture with no circular dependencies.
internal/resticis the innermost ring and nothing in it imports other internal packages.
Build system#
- Build tool: Custom
build.goscript (invoked viago run build.go), with a thin Makefile wrapper (make restic,make test,make clean) - Key targets:
go run build.go— builds theresticbinary with default tags (selfupdate,disable_grpc_modules), strips debug symbols, embeds version fromVERSION+git describego run build.go -T— builds and runs all testsgo run build.go --goos linux --goarch arm— cross-compilego run build.go --enable-cgo— enable CGO (off by default for static binaries)go run build.go --enable-pie— PIE build mode
- Docker: Yes, multi-stage (
docker/Dockerfile):- Stage 1:
golang:1.25-alpine— runsgo run build.go, produces binary - Stage 2:
alpine:latest— copies binary + addsca-certificates,fuse,openssh-client,tzdata,jq docker/Dockerfile.releasevariant exists for release builds
- Stage 1:
- CI: GitHub Actions (
.github/workflows/tests.yml,docker.yml) - Releases:
helpers/build-release-binariesandhelpers/prepare-releaseshell scripts; reproducible builds via-trimpathflag inbuild.go
Notable structural decisions#
One file per command in
cmd/restic/: Each of the ~25 commands has its owncmd_backup.go,cmd_restore.go, etc. This makescmd/restic/very large (83 files) but keeps command implementations isolated and easy to find. Integration tests co-locate alongside commands ascmd_backup_integration_test.go.internal/resticas dependency inversion anchor: The innermost domain package defines all canonical types and theBackendinterface. Every other package imports it; it imports nothing internal. This enforces the dependency rule architecturally.Backend subsystem as a decorator stack: The
internal/backendsubtree implements a clean decorator/wrapper pattern.cache,retry,limiter,sema,logger, anddryruneach wrap anyBackend, composable at wiring time inmain.go. This allows adding cross-cutting concerns (caching, retries, rate-limiting) without modifying drivers.No
pkg/— restic is a tool, not a library: The deliberate absence of any public packages signals that restic does not intend to be imported. This is a principled choice; users interact via CLI or REST server, not Go API.Build-tag-gated optional features:
cmd_mount.go/cmd_mount_disabled.go,cmd_debug.go/cmd_debug_disabled.go, andcmd_self_update.go/cmd_self_update_disabled.gopairs use build tags to include/exclude features (FUSE, debug, self-update) from the final binary, keeping the default binary small and dependency-free on platforms that don’t support FUSE.Custom
build.goscript instead of Makefile/goreleaser: Build logic is written in Go itself — version embedding, cross-compilation, tag management, CGO toggling — making it portable without requiring Make, shell, or goreleaser on the build machine.go run build.gois all that’s needed.