Skip to content

arkeep-io/arkeep

Arkeep

Your infra's Ark — back up everything, keep it yours.

Arkeep is an open-source backup management tool with a server/agent architecture. Deploy the server once, install lightweight agents on every machine you want to back up, and manage everything from a single web interface — built on top of Restic and Rclone.

🚧 Arkeep is in early access — core features are working and ready for testing. Not yet recommended for production use. Star the repository to follow progress.


Table of Contents


Why Arkeep?

Managing backups across multiple machines means juggling separate Restic configs, cron jobs, and shell scripts on every host. There is no central view, no unified alerting, and no easy way to verify everything ran successfully. Arkeep fixes this.

  • Centralized management — one dashboard for all your servers, no more managing backup configs machine by machine
  • Docker-aware — automatically discovers containers and volumes, adapts when you add or remove services without restarts
  • OIDC ready — integrates with Zitadel, Keycloak, Authentik, or any standard identity provider
  • Multi-destination — apply the 3-2-1 rule with multiple backup destinations per policy
  • End-to-end encryption — all backups are encrypted client-side; credentials are never stored in plain text
  • Real-time — live logs and status updates while backups run, accessible from any device
  • No vendor lock-in — built on Restic and Rclone; your data is always accessible even without Arkeep

Architecture

┌─────────────────────────────────────────┐
│              Arkeep Server              │
│  ┌──────────┐  ┌──────────────────────┐ │
│  │ REST API │  │     gRPC Server      │ │
│  │  :8080   │  │       :9090          │ │
│  └──────────┘  └──────────────────────┘ │
│  ┌────────────────────────────────────┐ │
│  │ Scheduler│Auth│DB│Notif│WebSocket  │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
         ▲                  ▲
         │ REST/WS          │ gRPC (persistent, Pull)
         │                  │
    ┌────┴────┐      ┌──────┴──────┐
    │   GUI   │      │   Agent(s)  │
    │  (PWA)  │      │  (one per   │
    └─────────┘      │   machine)  │
                     └─────────────┘

Server exposes a REST API for the GUI (port 8080) and a gRPC server for agents (port 9090). It handles scheduling, notifications, and stores all state in SQLite (default) or PostgreSQL.

Agent runs on each machine to be backed up. It initiates a persistent outbound gRPC connection to the server — it never listens on any port. This makes deployment behind NAT and corporate firewalls effortless.

GUI is a Vue 3 PWA served directly by the server as embedded static files. No separate web server required.


Features

Feature Status
Server/agent architecture
Web GUI (PWA, mobile-first)
Local auth + OIDC
Multi-destination (3-2-1)
Docker volume discovery
Pre/post hooks (pg_dump, etc.)
Integrity verification
Retention policies
Email + webhook notifications
Restore & restore test
Helm chart
Proxmox / VMware integration 🗓 planned
Bandwidth throttling 🗓 planned
BYOK encryption key management 🗓 planned

Supported Destinations

Type Notes
Local filesystem Direct path on the agent's host
S3-compatible AWS S3, MinIO, Backblaze B2, Cloudflare R2, and more
SFTP Any SSH server
Restic REST Server Self-hosted rest-server
Rclone 40+ backends including Google Drive, OneDrive, Azure Blob, and more

Deployment

Docker Compose

The simplest way to get started. All images are published to both GitHub Packages and Docker Hub.

Server only (GUI included):

curl -O https://raw.githubusercontent.com/arkeep-io/arkeep/main/deploy/docker/docker-compose.yml
curl -O https://raw.githubusercontent.com/arkeep-io/arkeep/main/deploy/docker/.env.example
cp .env.example .env
# Edit .env — at minimum set ARKEEP_SECRET_KEY and ARKEEP_AGENT_SECRET
docker compose up -d

The GUI is available at http://localhost:8080.

gRPC TLS with Docker:

The server generates a private CA and server certificate on first startup (auto-PKI). Agents auto-enroll via HTTP on first run and use mTLS from that point on — no manual cert configuration required.

For the server you can alternatively use an external certificate:

  • Reverse proxy (recommended): put Caddy or Nginx in front and let it handle TLS termination for both ports. No cert config inside the containers.
  • Direct TLS: mount a certificate and set ARKEEP_GRPC_TLS_CERT/ARKEEP_GRPC_TLS_KEY on the server container (see the commented lines in docker-compose.yml and .env.example).

The all-in-one compose file sets ARKEEP_GRPC_INSECURE=true on both server and agent automatically, since they share the same private Docker network and TLS adds no security benefit there.

Agent only (on the machines you want to back up):

curl -O https://raw.githubusercontent.com/arkeep-io/arkeep/main/deploy/docker/docker-compose.agent.yml
# Set ARKEEP_SERVER_ADDR and ARKEEP_AGENT_SECRET in your environment or .env
docker compose -f docker-compose.agent.yml up -d

All-in-one (server + agent on the same host):

curl -O https://raw.githubusercontent.com/arkeep-io/arkeep/main/deploy/docker/docker-compose.all.yml
docker compose -f docker-compose.all.yml up -d

For local filesystem destinations and restore behaviour with Docker, see Local destinations in Docker and Restore in Docker below.


Standalone Binary

Pre-built binaries for Linux, macOS, and Windows are available on the Releases page.

Server:

# Download and extract the server binary for your platform
curl -L https://github.com/arkeep-io/arkeep/releases/latest/download/arkeep-server_linux_amd64.tar.gz | tar xz

# Generate secrets
export ARKEEP_SECRET_KEY=$(openssl rand -hex 32)
export ARKEEP_AGENT_SECRET=$(openssl rand -hex 32)

./arkeep-server \
  --db-dsn /var/lib/arkeep/arkeep.db \
  --data-dir /var/lib/arkeep/data \
  --http-addr :8080 \
  --grpc-addr :9090

TLS (auto-PKI): by default the server generates a private CA and server certificate under --data-dir/grpc/ on first startup. Agents auto-enroll via POST /api/v1/agents/enroll on first run and use mTLS from then on — no manual cert management required.

To use an external certificate instead (e.g. Let's Encrypt via Caddy), pass --grpc-tls-cert and --grpc-tls-key. Auto-PKI is then skipped entirely.

Agent:

curl -L https://github.com/arkeep-io/arkeep/releases/latest/download/arkeep-agent_linux_amd64.tar.gz | tar xz

./arkeep-agent \
  --server-addr your-server:9090 \
  --agent-secret your-agent-secret \
  --state-dir /var/lib/arkeep-agent

Auto-enrollment: on first run the agent calls the server's HTTP API (derived from --server-addr with port 8080 by default) to obtain its client certificate. The CA cert and client cert are stored in --state-dir and reused on every subsequent startup — no re-enrollment unless you delete them.

Reverse proxy (Traefik, Nginx, Caddy): if the server's HTTP API is only reachable via HTTPS (port 8080 is not exposed directly), set --server-http-addr to the public HTTPS URL — e.g. --server-http-addr https://arkeep.example.com. The agent uses this address only for the one-time enrollment request; afterwards the mTLS certificates in --state-dir are reused on every restart.

Use --grpc-tls-ca only when connecting to a server that uses an external cert (not auto-PKI) signed by a non-system CA.

Server and agent on the same machine (no reverse proxy, no TLS):

If you are running both binaries on the same host and do not want TLS on the loopback interface, add --grpc-insecure to both. Communication stays on loopback and is never exposed to the network.

./arkeep-server \
  --db-dsn /var/lib/arkeep/arkeep.db \
  --data-dir /var/lib/arkeep/data \
  --grpc-insecure

./arkeep-agent \
  --server-addr localhost:9090 \
  --agent-secret your-agent-secret \
  --state-dir /var/lib/arkeep-agent \
  --grpc-insecure

For any setup where the gRPC port is reachable from other machines, always use TLS (the default).


Agent via systemd

A systemd unit file is provided at deploy/systemd/arkeep-agent.service.

# Copy the binary
sudo cp arkeep-agent /usr/local/bin/arkeep-agent
sudo chmod +x /usr/local/bin/arkeep-agent

# Copy and edit the unit file
sudo cp deploy/systemd/arkeep-agent.service /etc/systemd/system/
sudo systemctl daemon-reload

# Create an environment file with your credentials
sudo mkdir -p /etc/arkeep
sudo tee /etc/arkeep/agent.env > /dev/null <<EOF
ARKEEP_SERVER_ADDR=your-server:9090
ARKEEP_AGENT_SECRET=your-agent-secret
# For self-signed server certs only — leave empty for Let's Encrypt/trusted CAs:
# ARKEEP_GRPC_TLS_CA=/etc/arkeep/ca.crt
EOF
sudo chmod 600 /etc/arkeep/agent.env

sudo systemctl enable --now arkeep-agent
sudo journalctl -u arkeep-agent -f

Configuration

All options can be set via CLI flags or environment variables. CLI flags take precedence over environment variables when both are provided.

Server Configuration

Flag Env Default Description
--http-addr ARKEEP_HTTP_ADDR :8080 HTTP API and GUI listen address
--grpc-addr ARKEEP_GRPC_ADDR :9090 gRPC listen address for agents
--grpc-tls-cert ARKEEP_GRPC_TLS_CERT Path to PEM certificate for gRPC TLS (requires --grpc-tls-key)
--grpc-tls-key ARKEEP_GRPC_TLS_KEY Path to PEM private key for gRPC TLS (requires --grpc-tls-cert)
--db-driver ARKEEP_DB_DRIVER sqlite Database driver (sqlite or postgres)
--db-dsn ARKEEP_DB_DSN ./arkeep.db SQLite file path or PostgreSQL DSN
--secret-key ARKEEP_SECRET_KEY Required. Master key for AES-256-GCM credential encryption
--agent-secret ARKEEP_AGENT_SECRET Shared secret for gRPC agent authentication
--data-dir ARKEEP_DATA_DIR ./data Directory for RSA JWT keys and server state
--log-level ARKEEP_LOG_LEVEL info Log level (debug, info, warn, error)
--secure-cookies ARKEEP_SECURE_COOKIES false Set Secure flag on auth cookies (enable in production over HTTPS)
--telemetry ARKEEP_TELEMETRY true Send anonymous usage stats (opt-out)
--grpc-insecure ARKEEP_GRPC_INSECURE false Disable TLS for gRPC transport — development and same-machine deployments only

Generating secrets:

# Secret key (AES-256, must be kept stable — changing it invalidates all stored credentials)
openssl rand -hex 32

# Agent secret (any random string)
openssl rand -hex 24

PostgreSQL DSN example:

postgres://arkeep:password@localhost:5432/arkeep?sslmode=require

Agent Configuration

Flag Env Default Description
--server-addr ARKEEP_SERVER_ADDR localhost:9090 Server gRPC address (host:port)
--agent-secret ARKEEP_AGENT_SECRET Shared secret (must match server)
--state-dir ARKEEP_STATE_DIR ~/.arkeep Directory for agent state and extracted binaries
--docker-socket ARKEEP_DOCKER_SOCKET (platform default) Docker socket path
--log-level ARKEEP_LOG_LEVEL info Log level
--server-http-addr ARKEEP_SERVER_HTTP_ADDR (derived from --server-addr) Base URL of the server HTTP API used for enrollment. Required when the server is behind a TLS-terminating reverse proxy (e.g. https://arkeep.example.com). Default: --server-addr host with port 8080 over plain HTTP.
--grpc-tls-ca ARKEEP_GRPC_TLS_CA Path to CA certificate for gRPC TLS (only needed when the server uses an external, non-system-trusted cert)
--grpc-insecure ARKEEP_GRPC_INSECURE false Disable TLS for gRPC transport — development and same-machine deployments only
--docker-host-root ARKEEP_DOCKER_HOST_ROOT /hostfs (auto-detected inside Docker) Container path where the host filesystem is mounted. Auto-defaults to /hostfs inside Docker — no configuration required. Set only when using a custom mount point. See Local destinations in Docker.

Local destinations in Docker

When the agent runs in Docker, it can only write to paths that are mounted into the container. Mount the entire host filesystem once at /hostfs — the agent detects it is inside Docker and enables path translation automatically. No environment variable required.

Linux — add to the agent service volumes:

volumes:
  - /:/hostfs:ro   # :ro for backup-only; change to :rw to also restore to local paths

Windows (Docker Desktop) — one entry per drive letter:

volumes:
  - C:/:/hostfs/c:ro
  - D:/:/hostfs/d:ro   # add more drives as needed

With this in place you can type any native host path directly in the Arkeep UI as a backup destination (e.g. C:\Users\Filippo\Downloads or /home/user/backups) and the agent will translate it automatically. No per-directory bind-mounts required.

Advanced: if you mount the host filesystem at a path other than /hostfs, set ARKEEP_DOCKER_HOST_ROOT to your custom mount point. For binary, systemd, and Helm deployments leave it unset — paths are used as-is.

Restore in Docker

Restore to a custom path works out of the box — enter any host path in the UI (e.g. C:\Users\Filippo\Downloads\restore) and the agent writes the files there via the hostfs mount.

In-place restore (original location) behaviour depends on the /var/lib/docker/volumes mount mode:

Mount mode Local filesystem paths Docker volume paths
:ro (default) Restored normally Skipped — agent logs a warning. Change to :rw to enable.
:rw Restored normally Restored if the container is stopped; skipped with a warning if running.

To restore Docker volumes in-place:

  1. Change :ro to :rw for the /var/lib/docker/volumes mount in docker-compose.yml
  2. Stop the containers whose volumes you want to restore
  3. Run the in-place restore from the UI
  4. Restart the containers

Volumes of containers that are still running when the restore starts are automatically skipped with a log entry that names the container. The rest of the restore (local paths + stopped-container volumes) completes normally.


Development

Prerequisites

Tool Version Install
Go 1.26+ go.dev
Node.js 22+ nodejs.org
pnpm 9+ corepack enable
Docker any docker.com
Task latest go install github.com/go-task/task/v3/cmd/task@latest
protoc latest apt install protobuf-compiler / brew install protobuf
protoc-gen-go latest go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
protoc-gen-go-grpc latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Getting Started

git clone https://github.com/arkeep-io/arkeep
cd arkeep

# Download restic and rclone binaries (embedded into the agent at build time)
task deps:download

# Generate gRPC code from .proto definitions
task proto

# Start the server (http://localhost:8080, gRPC :9090)
task run:server

# In a separate terminal — start the GUI dev server with HMR (http://localhost:5173)
task run:gui

# In a separate terminal — start an agent pointing to the local server
task run:agent

The GUI dev server proxies API requests to the server, so you can work on frontend and backend simultaneously with hot reload on both sides.

Note: In development, the GUI runs as a separate Vite dev server on port 5173. In production (binary or Docker), the GUI is compiled and embedded directly inside the server binary — no separate process or web server is needed.

First login:

Open http://localhost:8080 in your browser. On first access you will be redirected to the setup page where you can create the initial admin account.

Project Structure

arkeep/
├── agent/                      # Agent binary
│   ├── cmd/agent/              # Entry point
│   └── internal/
│       ├── connection/         # gRPC client, job stream, state persistence
│       ├── executor/           # Job queue and execution orchestration
│       ├── restic/             # Restic/rclone wrapper (binary extraction, backup, forget, check)
│       ├── docker/             # Docker volume discovery
│       ├── hooks/              # Pre/post backup hook runner
│       └── metrics/            # Host metrics (CPU, RAM, disk) via gopsutil
├── server/                     # Server binary
│   ├── cmd/server/             # Entry point
│   └── internal/
│       ├── api/                # Chi router, HTTP handlers, middleware
│       ├── auth/               # JWT (RS256), local auth, OIDC
│       ├── db/                 # GORM setup, migrations, EncryptedString type
│       ├── repositories/       # Data access layer (explicit queries, no GORM Preload)
│       ├── grpc/               # gRPC server — receives agent streams, dispatches jobs
│       ├── agentmanager/       # In-memory registry of connected agents
│       ├── scheduler/          # gocron-based backup scheduler
│       ├── notification/       # Email (SMTP) and webhook notification senders
│       └── websocket/          # WebSocket hub for real-time GUI updates
├── shared/                     # Code shared between server and agent
│   ├── proto/                  # Protobuf definitions and generated Go code
│   └── types/                  # Shared type definitions
├── gui/                        # Vue 3 PWA frontend
│   └── src/
│       ├── components/         # Reusable UI components (shadcn-vue based)
│       ├── pages/              # Route-level page components
│       ├── stores/             # Pinia state stores
│       ├── services/           # API client, WebSocket client
│       ├── composables/        # Vue composables
│       ├── router/             # Vue Router configuration
│       └── types/              # TypeScript interfaces
├── deploy/
│   ├── docker/                 # Docker Compose files
│   ├── systemd/                # systemd unit file for the agent
│   └── helm/                   # Helm chart
├── go.work                     # Go workspace (agent + server + shared)
└── Taskfile.yml                # Task runner

Available Tasks

task build          # Build all binaries (server + agent, GUI included)
task build:server   # Build server binary (builds GUI first, then embeds it)
task build:agent    # Build agent binary (downloads restic + rclone first)
task build:gui      # Build the Vue GUI only (output to gui/dist/)
task test           # Run all tests (Go + GUI)
task lint           # Run linters (golangci-lint + vue-tsc)
task proto          # Regenerate gRPC code from .proto definitions
task tidy           # Tidy all Go modules
task clean          # Remove build artifacts

task run:server     # Run the server in development mode (GUI via task run:gui)
task run:agent      # Run the agent in development mode
task run:gui        # Run the GUI dev server with HMR (proxies API to :8080)

task deps:download  # Download restic and rclone binaries for the current platform

FAQ

Why Restic under the hood?

Restic is battle-tested, content-addressable, and has excellent deduplication. It handles encryption, chunking, and repository integrity natively. Arkeep adds the management layer on top — scheduling, multi-machine coordination, a GUI, notifications — without reinventing the storage engine.

Can I access my backups without Arkeep?

Yes. Since the underlying engine is Restic, you can always use the restic CLI directly against any repository that Arkeep has created. Your data is never locked in.

Does the agent need root privileges?

No. The agent runs as an unprivileged user. The only exception is Docker volume backup: to access volume mountpoints at /var/lib/docker/volumes/ on Linux, the agent needs to be in the docker group (or run as root). The Docker Compose deployment handles this automatically via the socket mount.

Why does the agent connect to the server, not the other way around?

Pull architecture means agents work behind NAT, firewalls, and dynamic IPs without any port-forwarding or VPN. The server never needs to reach out to agents — agents maintain a persistent gRPC stream and receive jobs through it.

SQLite or PostgreSQL?

SQLite is the default and works well for most deployments. Switch to PostgreSQL if you need high concurrency (many agents running jobs simultaneously) or if you want to run multiple server replicas behind a load balancer.

Is there a Kubernetes deployment?

Yes. A Helm chart is available in deploy/helm/. Set grpc.tls.existingSecret to the name of a TLS Secret (type kubernetes.io/tls) to enable TLS on the gRPC port — cert-manager with Let's Encrypt is the recommended approach. For simpler setups, Docker Compose on a single node is also supported.


Roadmap

v1.0 — Production-ready core

  • Restore & restore test
  • Helm chart
  • Comprehensive test coverage (server + agent + GUI)
  • Full documentation site

v1.x — Integrations

  • Proxmox backup (VM and LXC)
  • VMware vSphere integration

v2.0 — Advanced features

  • Bandwidth throttling
  • BYOK encryption key management

Telemetry

Arkeep sends anonymous usage statistics once per day to help prioritize development. No personal data, backup contents, credentials, or hostnames are ever transmitted.

What is sent: a stable random instance ID, Arkeep version, OS, number of connected agents, and number of active policies.

Aggregate stats are public at: https://telemetry.arkeep.io/stats

To opt out: set ARKEEP_TELEMETRY=false or pass --telemetry=false.


Contributing

Contributions are welcome. Please read CONTRIBUTING.md first.

License

Arkeep is licensed under the Apache License 2.0.

Copyright 2026 Filippo Crotti / Arkeep Contributors

About

Manage backups across all your servers from a single dashboard.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors