Standalone timestamped backups — files, folders, symlinks, hardlinks
bkp is a small, dependency-free CLI for fast local backups. Point it at files or directories and get timestamped copies: plain files are duplicated beside the original; directories become a single .tgz (ustar + gzip) that preserves symlinks and hardlinks. Unpack with bkp -x or standard tar.
Written in Odin. Compression uses an embedded miniz (gzip level 1). No system zlib, pigz, or zip required at runtime.
- Timestamped file copies —
notes.txt→notes.txt.20260714120000(likecp -p) - Directory archives —
project/→project.20260714120000.tgz - Symlinks & hardlinks — stored correctly in the tar stream (not flattened as ZIP often is)
- Extract mode —
bkp -x archive.tgz [dest]restores files, dirs, and links safely - Parallel entities —
-jprocesses multiple top-level paths at once - Quiet stdout — only
src -> dstlines; errors on stderr - Multi-arch releases — Linux amd64/arm64, macOS Apple Silicon, Windows amd64
Download from Releases, then:
chmod +x bkp-linux-amd64 # or the asset for your OS
./bkp-linux-amd64 notes.txt src/Requires Odin and a C compiler (cc / MSVC on Windows).
git clone https://github.com/macedot/bkp.git
cd bkp
make
./bkpbkp [-j N] [-c N] <file|directory|pattern> ...
bkp -x <archive.tgz> [dest_dir]
| Mode | Behavior |
|---|---|
| File | Copy to <path>.<timestamp> |
| Directory | Pack to <path>.<timestamp>.tgz (ustar + gzip level 1) |
-x |
Unpack .tgz / .tar.gz into dest_dir (default: .) |
| Flag | Default | Description |
|---|---|---|
-j N |
CPU count | Max paths to process in parallel |
-c N |
CPU count | Entry-prep workers while packing (gzip stream is serial) |
No arguments prints usage and exits with code 2. Exit 1 if any path fails.
Stdout is only mapping lines:
src -> dst
# Backup a file and a tree
./bkp notes.txt src/
# Parallel jobs
./bkp -j 4 -c 8 data/*.csv project/
# Shell globs (or let bkp expand patterns)
./bkp 'logs/*.log'
# Extract
./bkp -x project.20260714120000.tgz restored/
# Compatible with system tar
tar -tzf project.20260714120000.tgz
tar -xzf project.20260714120000.tgz -C restored/| Input | Output |
|---|---|
| Regular file | Byte copy next to the source, suffix .<YYYYMMDDHHMMSS> |
| Directory | Walk with lstat → ustar members (0 file, 5 dir, 2 symlink, 1 hardlink) → gzip → .tgz |
Hardlinks share an inode: the first copy stores data; later names reference it. Symlinks store the link target, not the pointed-to content.
Extract rejects path traversal (.., absolute names) before writing under the destination.
make testCovers file copy, nested tree, symlink, hardlink, tar -tzf, and bkp -x.
Publishing a GitHub Release runs .github/workflows/release.yml and attaches:
| Asset | Platform |
|---|---|
bkp-linux-amd64 |
Linux x86_64 |
bkp-linux-arm64 |
Linux ARM64 |
bkp-darwin-arm64 |
macOS Apple Silicon |
bkp-windows-amd64.exe |
Windows x86_64 |
| Path | Role |
|---|---|
src/ |
Odin sources (CLI, tar write/read, copy) |
vendor/miniz.* |
Embedded DEFLATE / gzip |
vendor/bkp_miniz_wrap.c |
Thin C API used from Odin |
build/ |
Local objects / static lib (not tracked) |
.github/workflows/ |
Multi-arch release builds |
bkp is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
Bundled miniz retains its own license (see vendor/MINIZ_LICENSE).