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 the restic binary
    • 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.go stubs

Package organization#

  • Internal packages (all under internal/):

    • internal/restic — domain core: canonical types (ID, Blob, Snapshot, Node, Pack, Index, FileType, Backend interface). The dependency anchor — everything imports this; it imports nothing internal.
    • internal/backendBackend interface + 14 pluggable storage drivers as subdirectories; decorator backends (cache, retry, limiter, sema, logger, dryrun) wrap any Backend implementation
    • internal/repository — higher-level repository operations: packing blobs, managing the index, encryption at rest, locking; imports internal/restic and internal/backend
    • internal/archiver — snapshot creation: filesystem traversal, chunking, deduplication, pack uploading
    • internal/restorer — snapshot restoration: parallel file writing, permission restoration
    • internal/fs — filesystem abstraction (fs.FS, virtual filesystems, node types for different OS)
    • internal/crypto — AES-256-CTR + Poly1305-AES encryption, key derivation
    • internal/checker — repository consistency checks (orphaned blobs, pack integrity, etc.)
    • internal/fuse — FUSE filesystem presenting snapshots as a virtual directory tree
    • internal/filter — glob-based file inclusion/exclusion pattern matching
    • internal/feature — compile-time and runtime feature flag support (RESTIC_FEATURES env var)
    • internal/ui/* — layered UI: termstatus (raw terminal), terminal (output routing), backup/restore (domain-specific progress), progress, table, signals
    • internal/global — global flags struct (Options), context creation, version string, profiling
    • internal/debug — build-tag-gated debug logging (debug tag enables it)
    • internal/errorserrors.IsFatal(), errors.Wrap(), sentinel error helpers
    • internal/migrations — repository format migration handlers
    • internal/options — extended key=value options parsing for backend configuration
    • internal/bloblru — LRU cache for frequently accessed blobs
    • internal/selfupdate — binary self-update from GitHub releases
    • internal/dump — dump snapshot content to stdout (tar or zip)
    • internal/walker — tree traversal over repository snapshots
    • internal/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/restic is the innermost ring and nothing in it imports other internal packages.

Build system#

  • Build tool: Custom build.go script (invoked via go run build.go), with a thin Makefile wrapper (make restic, make test, make clean)
  • Key targets:
    • go run build.go — builds the restic binary with default tags (selfupdate, disable_grpc_modules), strips debug symbols, embeds version from VERSION + git describe
    • go run build.go -T — builds and runs all tests
    • go run build.go --goos linux --goarch arm — cross-compile
    • go 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 — runs go run build.go, produces binary
    • Stage 2: alpine:latest — copies binary + adds ca-certificates, fuse, openssh-client, tzdata, jq
    • docker/Dockerfile.release variant exists for release builds
  • CI: GitHub Actions (.github/workflows/tests.yml, docker.yml)
  • Releases: helpers/build-release-binaries and helpers/prepare-release shell scripts; reproducible builds via -trimpath flag in build.go

Notable structural decisions#

  1. One file per command in cmd/restic/: Each of the ~25 commands has its own cmd_backup.go, cmd_restore.go, etc. This makes cmd/restic/ very large (83 files) but keeps command implementations isolated and easy to find. Integration tests co-locate alongside commands as cmd_backup_integration_test.go.

  2. internal/restic as dependency inversion anchor: The innermost domain package defines all canonical types and the Backend interface. Every other package imports it; it imports nothing internal. This enforces the dependency rule architecturally.

  3. Backend subsystem as a decorator stack: The internal/backend subtree implements a clean decorator/wrapper pattern. cache, retry, limiter, sema, logger, and dryrun each wrap any Backend, composable at wiring time in main.go. This allows adding cross-cutting concerns (caching, retries, rate-limiting) without modifying drivers.

  4. 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.

  5. Build-tag-gated optional features: cmd_mount.go / cmd_mount_disabled.go, cmd_debug.go / cmd_debug_disabled.go, and cmd_self_update.go / cmd_self_update_disabled.go pairs 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.

  6. Custom build.go script 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.go is all that’s needed.