В этой статье рассмотрим установку и настройку системы резервного копирования Arkeep с помощью Docker Compose.
🖐️Эй!
Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике — заглядывайте в Вороний чат @r4ven_me_chat🧐.
Предисловие

Arkeep - это Open source система централизованного резервного копирования, построенная на базе таких замечательных инструментов, как Restic и Rclone.
Существует множество различных инструментов бэкапа, использующих данные утилиты. Но все они не являются “полноценно централизованными”. Это довольно актуальная проблема в мире Open source.
И как гласит философия программистов: если нет нужного тебе инструмента, напиши его сам. Что и сделал разработчик Arkeep. Скрин с Реддита:

Arkeep имеет агент-серверную архитектуру. То есть вы настраиваете сервер Arkeep, затем устанавливаете на нужные вам хосты arkeep-agent и подключаете их к серверу. После этого вы можете управлять настройкой бэкапа этих хостов. В качестве хранилища можно использовать всё то, с чем умеют работать Restic и Rclone.
Схема резервного копирования с Arkeep
В данной статье мы рассмотрим настройку резервного копирования Linux-хостов под управлением Debian 13 с помощью Arkeep (Restic) в объектное S3-хранилище на базе RustFS.
Доступ ко всем ресурсам организуем через обратный proxy-сервер Angie. К Web GUI-панелям подключаться будем по HTTPS, для чего настроим автоматическое получение TLS-сертификатов от Let’s Encrypt.
Ниже представлена схема будущей инфраструктуры:

Тут важно понимать один момент:
- Web GUI работает по HTTP/HTTPS;
- агенты подключаются отдельно по gRPC.
Взаимодействие агентов и сервера Arkeep мы будем настраивать через проксирование gRPC с помощью директивы grpc_proxy. При этом подключения агентов до прокси-сервера будут осуществляться с помощью HTTPS, а от Angie до сервера Arkeep уже по обычному HTTP+gRPC.
Вводные данные
ПО, используемое в статье:
| ПО | Версия |
|---|---|
| Debian | 13 |
| Docker Engine | 28 |
| Angie | 1.9.1 |
| Arkeep | 0.4.5 |
| RustFS | 1.0.0-beta.8 |
Доменная схема:
arkeep.r4ven.me- Web GUI сервера Arkeep;arkeep-grpc.r4ven.me- gRPC endpoint для Arkeep агентов;rustfs.r4ven.me- Web GUI хранилища S3;rustfs-s3.r4ven.me- S3 endpoint для сохранения бэкапов.
Подготовка инфраструктуры
Установка бэкап-сервера Arkeep
Подключаемся к серверу arkeep.r4ven.me с помощью SSH и переходим на УЗ root с помощью sudo:
ssh ivan@arkeep.r4ven.me
sudo -iСоздаём общую сеть Docker:
docker network create --opt com.docker.network.bridge.name=br-arkeep --opt com.docker.network.enable_ipv6=false --driver bridge --subnet 172.20.20.0/24 --gateway 172.20.20.1 arkeep_netСоздаём директорию проекта Arkeep:
install -m 700 -d /etc/compose
mkdir -vp /etc/compose/arkeep && cd /etc/compose/arkeepТеперь создаём compose-файл, например, с помощью редактора vim:
vim ./compose.yamlНаполняем:
---
# https://github.com/arkeep-io/arkeep/tree/main/deploy/docker
networks:
arkeep_net:
external: true
volumes:
arkeep_server:
name: arkeep_server
arkeep_agent:
name: arkeep_agent
services:
arkeep_server:
image: ghcr.io/arkeep-io/arkeep-server:v0.4.5
container_name: arkeep-server
restart: unless-stopped
stop_grace_period: 1m
cpus: 1
mem_limit: 1G
hostname: arkeep-server
env_file: ./.env
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health/ready"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
expose:
- "8080" # REST API + GUI
- "9090" # gRPC (agents)
volumes:
- arkeep_server:/var/lib/arkeep/
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- arkeep_net
arkeep_agent:
image: ghcr.io/arkeep-io/arkeep-agent:v0.4.5
depends_on:
arkeep_server:
condition: service_healthy
container_name: arkeep-agent
restart: unless-stopped
stop_grace_period: 1m
cpus: 1
mem_limit: 1G
hostname: arkeep-agent
environment:
TZ: "${TZ:-Europe/Moscow}"
ARKEEP_SERVER_ADDR: "arkeep-server:9090"
ARKEEP_AGENT_SECRET: "${ARKEEP_AGENT_SECRET}"
ARKEEP_STATE_DIR: "/var/lib/arkeep-agent"
ARKEEP_LOG_LEVEL: "${ARKEEP_LOG_LEVEL:-info}"
ARKEEP_GRPC_INSECURE: "${ARKEEP_GRPC_INSECURE:-false}"
volumes:
- arkeep_agent:/var/lib/arkeep-agent/
- /:/hostfs/:ro # :ro for backup-only; change to :rw to also restore to local paths
- /var/run/docker.sock:/var/run/docker.sock:ro
- /var/lib/docker/volumes/:/var/lib/docker/volumes/:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- arkeep_netОтдельно стоит сказать про монтирование корня файловой системы хоста: /:/hostfs/:ro. Монтирование в режиме только для чтения (ro) позволит выполнять бэкап любой точки системы. Если вы хотите также иметь возможность восстанавливать файлы в исходные директории - в таком случае необходимо монтировать в режиме rw (просто уберите ro). И т.к. Arkeep-агент в нашем случае будет работать от имени пользователя root, это подразумевает полный доступ агента к файлам системы. Имейте это в виду. Продолжим.
Создаём env файл:
vim ./.envНаполняем:
# https://github.com/arkeep-io/arkeep/tree/main/deploy/docker
TZ=Europe/Moscow
ARKEEP_SECRET_KEY=
ARKEEP_AGENT_SECRET=
ARKEEP_DATA_DIR=/var/lib/arkeep/data
ARKEEP_DB_DRIVER=sqlite
ARKEEP_DB_DSN=/var/lib/arkeep/arkeep.db
ARKEEP_LOG_LEVEL=info
ARKEEP_SERVER_ADDR=arkeep-grpc.r4ven.me:443
ARKEEP_SERVER_HTTP_ADDR=https://arkeep.r4ven.me
ARKEEP_GRPC_INSECURE=true
ARKEEP_TELEMETRY=trueОписание параметров:
TZ- timezone контейнера;ARKEEP_SECRET_KEY- ключ подписи JWT токенов;ARKEEP_AGENT_SECRET- общий секрет сервера и агентов;ARKEEP_DATA_DIR- директория данных внутри контейнера;ARKEEP_DB_DRIVER- тип БД, в нашем случае Sqlite, но можно также использовать Postgres;ARKEEP_DB_DSN- путь к файлу БД;ARKEEP_LOG_LEVEL- уровень логирования;ARKEEP_SERVER_ADDR- адрес сервера для подключения агентов по gRPC (обязательно безhttp/https);ARKEEP_SERVER_HTTP_ADDR- внешний HTTPS-адрес Web GUI;ARKEEP_GRPC_INSECURE=true- отключён TLS-режим, т.к. за терминацию TLS у нас будет отвечать Angie;ARKEEP_TELEMETRY- включить/отключить отправку анонимной статистики, про неё чуть подробнее в конце статьи.
☝️ ARKEEP_AGENT_SECRET должен совпадать на сервере и всех агентах.
Генерируем секреты рекомендуемой длины:
sed -iE "s/^ARKEEP_SECRET_KEY=.*/ARKEEP_SECRET_KEY=$(openssl rand -hex 32)/" ./.env
sed -iE "s/^ARKEEP_AGENT_SECRET=.*/ARKEEP_AGENT_SECRET=$(openssl rand -hex 24)/" ./.env☝️ Как уже говорил, для выпуска сертификатов должны быть созданы DNS-записи для адресов, определённых в параметрах ARKEEP_SERVER_ADDR и ARKEEP_SERVER_HTTP_ADDR.
Запускаем стек:
docker compose up -d && docker compose logs -f💡 Для выхода из режима просмотра логов нажмите Ctrl+c.
Проверяем:
docker compose ps
docker compose volumesЕсли всё нормально, увидим примерно такой вывод:

Сразу же настроим автозапуск с помощью Systemd:
cat > ./arkeep.service << EOF
[Unit]
Description=Arkeep service
Requires=docker.service
After=docker.service
[Service]
Restart=always
RestartSec=5
User=root
Group=root
WorkingDirectory=$(realpath ./)
ExecStart=/usr/bin/docker compose up --remove-orphans
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
EOFchmod 600 ./arkeep.service
ln -s $(realpath ./arkeep.service) /etc/systemd/system
systemctl daemon-reload
systemctl enable --now arkeep
systemctl status arkeep
Теперь сервисом arkeep можно управлять утилитой systemctl:
systemctl stop arkeep
systemctl start arkeep
systemctl restart arkeep
systemctl status arkeepУстановка S3-хранилища RustFS
RustFS - S3-совместимое хранилище, установку, настройку и использование которого мы рассматривали в прошлой статье.
Создаём директорию проекта:
mkdir -vp /etc/compose/rustfs && cd /etc/compose/rustfsТеперь создаём compose-файл:
vim ./compose.yamlНаполняем:
---
# https://github.com/rustfs/rustfs
networks:
arkeep_net:
external: true
volumes:
rustfs:
name: rustfs
services:
rustfs:
image: rustfs/rustfs:1.0.0-beta.8
container_name: rustfs
restart: unless-stopped
stop_grace_period: 30s
cpus: 1
mem_limit: 1G
hostname: rustfs
env_file: ./.env
healthcheck:
test:
["CMD", "sh", "-c", "curl -f http://127.0.0.1:9000/health && curl -f http://127.0.0.1:9001/rustfs/console/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
expose:
- "9000" # S3 API port
- "9001" # Console port
volumes:
- rustfs:/data/
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- arkeep_netСоздаём env-файл:
vim ./.envНаполняем:
# https://docs.rustfs.com/installation/docker/#complete-parameter-configuration-example
TZ=Europe/Moscow
# RUSTFS_VOLUMES=/data/rustfs{0..3} # Define 4 storage volumes
RUSTFS_ADDRESS=0.0.0.0:9000
RUSTFS_CONSOLE_ENABLE=true
RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
RUSTFS_CORS_ALLOWED_ORIGINS=https://rustfs.r4ven.me
RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=https://rustfs.r4ven.me
RUSTFS_ACCESS_KEY=
RUSTFS_SECRET_KEY=
RUSTFS_OBS_LOGGER_LEVEL=infoГде:
TZ- таймзона контейнера;RUSTFS_ADDRESS- адрес и порт S3 API;RUSTFS_CONSOLE_ENABLE- включение веб-консоли;RUSTFS_CONSOLE_ADDRESS- адрес и порт веб-консоли;RUSTFS_CORS_ALLOWED_ORIGINS- разрешённые источники (CORS) для S3 API;RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS- разрешённые источники (CORS) для веб-консоли;RUSTFS_ACCESS_KEY- S3 Access Key (логин);RUSTFS_SECRET_KEY- S3 Secret Key (пароль);RUSTFS_OBS_LOGGER_LEVEL- уровень логирования (error,warn,info,debug,trace).
☝️ RUSTFS_ACCESS_KEY и RUSTFS_SECRET_KEY будут использоваться как логин и пароль для авторизации в веб-консоли RustFS.
☝️ Как уже говорил, для выпуска сертификатов должны быть созданы DNS-записи для нужных адресов, в моём примере это rustfs.r4ven.me и rustfs-s3.r4ven.me.
Задаём “логин” (замените на свой) и генерируем “пароль”:
sed -iE "s/^RUSTFS_ACCESS_KEY=.*/RUSTFS_ACCESS_KEY=ivan/" ./.env
sed -iE "s/^RUSTFS_SECRET_KEY=.*/RUSTFS_SECRET_KEY=$(openssl rand -hex 32)/" ./.envЗапускаем стек:
docker compose up -d && docker compose logs -f💡 Для выхода из режима просмотра логов нажмите Ctrl+c.
Проверяем:
docker compose ps
docker compose volumesЕсли всё нормально, увидим примерно такой вывод:

Сразу же настроим автозапуск с помощью Systemd:
cat > ./rustfs.service << EOF
[Unit]
Description=RustFS S3 service
Requires=docker.service
After=docker.service
[Service]
Restart=always
RestartSec=5
User=root
Group=root
WorkingDirectory=$(realpath ./)
ExecStart=/usr/bin/docker compose up --remove-orphans
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
EOFchmod 600 ./rustfs.service
ln -s $(realpath ./rustfs.service) /etc/systemd/system
systemctl daemon-reload
systemctl enable --now rustfs
systemctl status rustfs
Теперь сервисом rustfs можно управлять утилитой systemctl:
systemctl stop rustfs
systemctl start rustfs
systemctl restart rustfs
systemctl status rustfsУстановка обратного прокси-сервера Angie
Angie - это форк известного Nginx (только лучше!) от наших соотечественников, которые ранее работали над Nginx.
Создаём директорию проекта:
mkdir -vp /etc/compose/angie && cd /etc/compose/angieСоздаём файл описания Docker-сервиса:
vim ./compose.yamlНаполняем:
---
# https://angie.software/angie/docs/installation/docker/
networks:
arkeep_net:
external: true
services:
angie:
image: docker.angie.software/angie:1.9.1-alpine
container_name: angie
restart: unless-stopped
stop_grace_period: 1m
cpus: 1
mem_limit: 1G
hostname: angie
environment:
TZ: Europe/Moscow
volumes:
- ./config/angie.conf:/etc/angie/angie.conf:ro
- ./config/http.d/:/etc/angie/http.d/:ro
- ./certs/:/var/lib/angie/acme/
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "80:80/tcp"
- "443:443/tcp"
- "443:443/udp"
networks:
- arkeep_netСоздаём конфиг:
mkdir -vp ./config
vim ./config/angie.confНаполняем:
user angie;
worker_processes auto;
worker_rlimit_nofile 65536;
error_log /var/log/angie/error.log notice;
pid /run/angie/angie.pid;
events {
worker_connections 65536;
}
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
include /etc/angie/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format extended '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" rt="$request_time" '
'"$http_user_agent" "$http_x_forwarded_for" '
'h="$host" sn="$server_name" ru="$request_uri" u="$uri" '
'ucs="$upstream_cache_status" ua="$upstream_addr" us="$upstream_status" '
'uct="$upstream_connect_time" urt="$upstream_response_time"';
access_log /var/log/angie/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 20;
keepalive_requests 1000;
server_tokens off;
# DNS resolver for ACME.
resolver 8.8.8.8 8.8.4.4 ipv6=off valid=300s;
acme_client le https://acme-v02.api.letsencrypt.org/directory
email=kar-kar@r4ven.me
key_type=ecdsa
renew_before_expiry=30d
# renew_on_load=on
;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
upstream arkeep_server {
server arkeep-server:9090;
}
upstream arkeep_console {
server arkeep-server:8080;
}
upstream rustfs_server {
server rustfs:9000;
}
upstream rustfs_console {
server rustfs:9001;
}
server {
listen 80;
server_name
arkeep-grpc.r4ven.me
arkeep.r4ven.me
rustfs-s3.r4ven.me
rustfs.r4ven.me
;
location /.well-known/acme-challenge/ {
root /usr/share/angie/html/;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
http3 on;
server_name arkeep-grpc.r4ven.me;
acme le;
ssl_certificate $acme_cert_le;
ssl_certificate_key $acme_cert_key_le;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
grpc_pass grpc://arkeep_server;
grpc_set_header Host $host;
grpc_set_header X-Real-IP $remote_addr;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_connect_timeout 10s;
grpc_send_timeout 1h;
grpc_read_timeout 1h;
grpc_socket_keepalive on;
}
}
server {
listen 443 ssl;
http2 on;
http3 on;
server_name arkeep.r4ven.me;
acme le;
ssl_certificate $acme_cert_le;
ssl_certificate_key $acme_cert_key_le;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
client_max_body_size 0;
location / {
proxy_pass http://arkeep_console;
proxy_http_version 1.1;
proxy_request_buffering off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 10s;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
}
}
server {
listen 443 ssl;
http2 on;
http3 on;
server_name rustfs-s3.r4ven.me;
acme le;
ssl_certificate $acme_cert_le;
ssl_certificate_key $acme_cert_key_le;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
client_max_body_size 0;
location / {
proxy_pass http://rustfs_server;
proxy_http_version 1.1;
proxy_request_buffering off;
proxy_buffering off;
proxy_cache off;
proxy_max_temp_file_size 0;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-Ssl on;
proxy_connect_timeout 10s;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
}
}
server {
listen 443 ssl;
http2 on;
http3 on;
server_name rustfs.r4ven.me;
acme le;
ssl_certificate $acme_cert_le;
ssl_certificate_key $acme_cert_key_le;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://rustfs_console;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
include /etc/angie/http.d/*.conf;
}☝️ Обязательно замените email=kar-kar@r4ven.me в настройках acme и значения server_name для каждого виртуального хоста.
Запускаем стек:
docker compose up -d && docker compose logs -fПроверяем Angie и все остальные сервисы:
docker compose ps
docker psЕсли всё нормально, увидим примерно такой вывод:

Сразу же настроим автозапуск с помощью Systemd:
cat > ./angie.service << EOF
[Unit]
Description=Angie proxy service
Requires=docker.service
After=docker.service
[Service]
Restart=always
RestartSec=5
User=root
Group=root
WorkingDirectory=$(realpath ./)
ExecStart=/usr/bin/docker compose up --remove-orphans
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
EOFchmod 600 ./angie.service
ln -s $(realpath ./angie.service) /etc/systemd/system
systemctl daemon-reload
systemctl enable --now angie
systemctl status angie
Теперь сервисом angie можно управлять утилитой systemctl:
systemctl stop angie
systemctl start angie
systemctl restart angie
systemctl status angie❗️ Осторожно
Сразу же после успешного запуска Angie обязательно выполните следующий шаг, если, как и в этом примере, панель управления Arkeep будет доступна из интернета.
Первым же делом после запуска обратного прокси создаём admin account в Arkeep:
Открываем веб-браузер, переходим по адресу, который указывали для GUI панели и задаём реквизиты для входа администратора:
📝 Обратите внимание на валидное HTTPS подключение.
https://arkeep.r4ven.me/
После входа попадём на дашборд Arkeep:

В разделе “Agents” должны увидеть нашего локального агента, который поднимается вместе с сервером:

Установка и настройка Arkeep агентов на app-серверах
Теперь переходим на хосты, которые нужно бэкапить:
app1.r4ven.meapp2.r4ven.meapp3.r4ven.me
Создаём директорию:
install -m 700 -d /etc/compose
mkdir -vp /etc/compose/arkeep_agent && cd /etc/compose/arkeep_agentСоздаём compose-файл агента:
vim ./compose.yamlНаполняем:
---
# https://github.com/arkeep-io/arkeep/tree/main/deploy/docker
volumes:
arkeep_agent:
name: arkeep_agent
services:
arkeep_agent:
image: ghcr.io/arkeep-io/arkeep-agent:v0.4.5
container_name: arkeep-agent-app1
restart: unless-stopped
stop_grace_period: 1m
cpus: 1
mem_limit: 1G
hostname: arkeep-agent-app1
env_file: ./.env
volumes:
- arkeep_agent:/var/lib/arkeep-agent/
- /:/hostfs/:ro # :ro for backup-only; change to :rw to also restore to local paths
- /var/run/docker.sock:/var/run/docker.sock:ro
- /var/lib/docker/volumes/:/var/lib/docker/volumes/:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:roТут стоит отдельно пояснить bind mount /hostfs: именно через него агент получает доступ к файловой системе хоста для backup. Режима ro достаточно только для создания бэкапов. Для восстановления файлов в исходное расположение необходим доступ в режиме rw.
Создаём env-файл:
vim ./.envНаполняем:
# https://github.com/arkeep-io/arkeep/tree/main/deploy/docker
TZ=Europe/Moscow
ARKEEP_SERVER_ADDR=arkeep-grpc.r4ven.me:443
ARKEEP_SERVER_HTTP_ADDR=https://arkeep.r4ven.me
ARKEEP_AGENT_SECRET=
ARKEEP_STATE_DIR=/var/lib/arkeep-agent
ARKEEP_LOG_LEVEL=info
ARKEEP_GRPC_INSECURE=falseОписание параметров:
TZ- timezone контейнера;ARKEEP_SERVER_ADDR- адрес сервера для подключения агентов по gRPC (обязательно безhttp/https);ARKEEP_SERVER_HTTP_ADDR- внешний HTTPS-адрес Web GUI;ARKEEP_AGENT_SECRET- общий секрет;ARKEEP_STATE_DIR- директория данных внутри контейнера;ARKEEP_LOG_LEVEL- уровень логирования;ARKEEP_GRPC_INSECURE=false- включён TLS-режим (TLS идёт до Angie, а далее без шифрования).
☝️ ARKEEP_AGENT_SECRET должен совпадать со значением, указанным на сервере Arkeep.
☝️ В случае использования reverse proxy перед Web-интерфейсом, параметр ARKEEP_SERVER_HTTP_ADDR обязателен.
Запускаем контейнер:
docker compose up -d && docker compose logs -fПри успешном подключении агента к серверу мы должны увидеть эти две строки:
arkeep-agent-app1 | {"level":"info","ts":1781279912.8271096,"logger":"connection","caller":"connection/manager.go:427","msg":"registered with server","agent_id":"019ebc8e-7375-7ec1-b345-9d309cc983e8","agent_name":"arkeep-agent-app1"}
arkeep-agent-app1 | {"level":"info","ts":1781279912.827225,"logger":"connection","caller":"connection/manager.go:527","msg":"job stream open","agent_id":"019ebc8e-7375-7ec1-b345-9d309cc983e8"}А в консоли управления Arkeep появится новый агент:

Сразу же настроим автозапуск агента с помощью Systemd:
cat > ./arkeep-agent.service << EOF
[Unit]
Description=Arkeep agent service
Requires=docker.service
After=docker.service
[Service]
Restart=always
RestartSec=5
User=root
Group=root
WorkingDirectory=$(realpath ./)
ExecStart=/usr/bin/docker compose up --remove-orphans
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
EOFchmod 600 ./arkeep-agent.service
ln -s $(realpath ./arkeep-agent.service) /etc/systemd/system
systemctl daemon-reload
systemctl enable --now arkeep-agent
systemctl status arkeep-agent
Теперь сервисом arkeep-agent можно также управлять утилитой systemctl:
systemctl stop arkeep-agent
systemctl start arkeep-agent
systemctl restart arkeep-agent
systemctl status arkeep-agentНастройка RustFS
Для подготовки и настройки S3-хранилища, пожалуйста, изучите мою отдельную статью:
Для дальнейшей настройки нам понадобятся следующие данные:
endpoint- адрес S3-хранилища, в нашем случае этоrustfs-s3.r4ven.me;bucket- бакет, в который мы будем складывать бэкапы;access_key_idиsecret_access_key- секреты для доступа к бакету с политикойreadwrite.
Настройка Arkeep
Сперва настроим удалённое хранилище, куда будем складывать резервные копии.
Настройка Destination - S3
Переходим в раздел “Destinations”:

Нажимаем “New Destination”, в параметре “Type” указываем желаемый тип хранилища, в нашем случае это “S3/Object Storage”, имя бакета и задаём реквизиты для подключения:

Новое хранилище появится в списке:

Немного про другие типы Destination
Помимо S3, бэкапы можно сохранять в локальную директорию, в директорию доступную по SFTP, на REST Server или в любое другое удалённое хранилище, доступ к которому можно настроить с помощью Rclone (а это 40+ провайдеров):

Для использования rclone в случае arkep-agent‘а, запущенного в Docker, необходимо доустановить rclone и примонтировать к нему конфиг с нужным удалённым хранилищем. Пример для WebDav:
cat > ./rclone.conf << EOF
[webdav-backup]
type = webdav
url = https://cloud.example.com/remote.php/dav/files/USER
vendor = nextcloud
user = USER
pass = OBSCURED_PASSWORD
EOFА compose файл будет выглядеть так:
---
services:
arkeep_agent:
image: ghcr.io/arkeep-io/arkeep-agent:v0.4.5
container_name: arkeep-agent-rclone
restart: unless-stopped
hostname: arkeep-agent-rclone
env_file: ./.env
command: sh -c 'apk add rclone; exec arkeep-agent'
volumes:
- ./data/:/var/lib/arkeep-agent/
- /:/hostfs/:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
- /var/lib/docker/volumes/:/var/lib/docker/volumes/:ro
- ./rclone.conf:/root/.config/rclone/rclone.conf:roВ настройках же “Destination” в панели Arkeep нужно будет указать тип бэкапа - “Rclone” и, опционально, путь к директории хранения:

Добавление Policy
Следующим шагом настроим политику, а точнее задание бэкапирования. Переходим в “Policies” и нажимаем “New Policy”:

Тут указываем все необходимые параметры: имя задания, агента и пароль для шифрования файлов в удалённом хранилище:

Далее указываем источники бэкапа: в нашем примере будем бэкапить директорию /etc и Docker volume Arkeep агента на хосте app1.r4ven.me:

Следующим настраивается расписание выполнения задания в формате crontab, а также время хранения и количество резервных копий:
💡 Совет
Если плохо понимаете формат Crontab, рекомендую этот замечательный интерактивный сайт для расчёта времени выполнения.

В самом конце выбираем удалённое хранилище (наш S3) и при необходимости задаём исключения:

Запуск резервного копирования

Для проверки работоспособности, пробуем запустить созданное нами задание бэкапа вручную:

Успех:

В разделе “Snapshots” появится новый бэкап:

В нашем S3 rustfs.r4ven.me можно будет увидеть файлы репозитория Restic:


Восстановление из бэкапа
Как уже говорилось ранее, по умолчанию файловая система хоста монтируется в режиме только чтения.
В compose.yaml это место подписано так: :ro for backup-only; change to :rw to also restore to local paths.
volumes:
- /:/hostfs:roПри добавлении возможности агенту производить запись в файловую систему хоста измените параметр выше на:
volumes:
- /:/hostfs:rwИ перезапустите агента:
systemctl restart arkeep-agentНу или примонтируйте отдельную директорию с возможностью записи.
Переходим в раздел “Snapshots”, выбираем подходящий, далее три точки и “Restore”:

Тут нам на выбор доступно два режима восстановления:
- Custom path
- Original path
Рассмотрим каждый из них.
Custom path
В этом варианте нам также доступен выбор: восстановить всё в указанную директорию - мы явно задаём свой путь в файловой системе хоста (без hostfs) куда будем восстанавливать файлы из бэкапа или восстановить отдельные файлы - тут можно выделить отдельные файлы/директории и восстановить только их.
- Восстановить всё в указанную директорию

- Восстановить отдельные файлы

Для запуска восстановления нажимаем “Start restore”. При успехе увидим:

Проверяем в файловой системе хоста:

Отлично!
Original path
При использовании режима восстановления “Original path” файлы из бэкапа будут восстановлены по исходному пути, где они располагались во время бэкапа. Данная процедура может “перезатереть” файлы, изменённые уже после бэкапа. О чём нас и предупреждают:

Запускаем восстановление и… успешно:

Возможные проблемы
В ходе изучения Arkeep я столкнулся с некоторыми проблемами/ошибками. Укажу их тут, вдруг для кого-то окажется полезным.
- read only filesystem
Чаще всего можно столкнуться именно с такой ошибкой:

Как указано в сообщении, директория назначения недоступна для записи. Про этот момент я рассказывал ранее, в начале шага восстановления из бэкапа.
- enrollment failed
При подключении агентов:
arkeep-agent-app1 | {... "msg":"enrollment failed — check --server-http-addr and --agent-secret","error":"enroll: server returned 405 Method Not Allowed" ...}Сообщение enrollment failed — server returned 405 Method Not Allowed возникает из-за того, что агент пытается выполнить процедуру автоматической выдачи клиентского сертификата (enrollment) через HTTP API, однако на сервере включён режим ARKEEP_GRPC_INSECURE=true, в котором этот механизм не используется.
Ошибку можно игнорировать, если после неё агент успешно подключается к серверу по gRPC, что подтверждается сообщениями registered with server и job stream open. При этом соединение между агентом и сервером остаётся защищённым TLS-сертификатом Angie на порту 443, а незашифрованный трафик существует только внутри локальной Docker-сети сервера.
- failed to verify certificate
tls: failed to verify certificate:
x509: certificate signed by unknown authorityОбычно причины такие:
не указан
ARKEEP_SERVER_HTTP_ADDR;агент подключается не к тому hostname;
используется самоподписанный сертификат reverse proxy;
TLS был включён/отключён только на одной стороне.
certificate is valid for arkeep-grpc
x509: certificate is valid for arkeep-grpc, not arkeep.r4ven.meОбычно она возникает, если:
- ранее запускался контейнер с другим hostname;
- сохранились старые сертификаты;
- изменился публичный адрес сервера.
В таком случае проще всего остановить сервер, удалить директорию Docker volume с данными (если вы только что его развернули) и заново запустить контейнер.
Сертификаты будут перевыпущены автоматически.
Пару слов про добровольную телеметрию
По умолчанию Arkeep собирает анонимную телеметрию. Данное действие можно отключить, но сперва прочтите цитату разработчика:
https://github.com/arkeep-io/arkeep#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=falseor pass--telemetry=false.
Послесловие
В целом Arkeep мне видится очень удобным и полезным инструментом бэкапа с крутым потенциалом. Надеюсь проект станет популярным и у разработчика будет мотивация его развивать и улучшать. Идея использовать Restic и Rclone проста и гениальна 👍.
Мне кажется у проекта незаслуженно мало звёзд и форков. Если инструмент вам зашёл и у вас есть аккаунт на GitHub, пожалуйста, поставьте репозиторию Arkeep звёздочку ⭐.
Перед тем, как написать эту статью, я проводил свои тесты на версии 0.4.3. На этапе восстановления я заметил один баг: при выборе режима восстановления поле было пустое 🤷♂️. Ещё был нюанс с некорректным сообщением типа “error” в логе агента.
В итоге я создал два issue в репозитории Arkeep и разработчик довольно оперативно отреагировал на них, пофиксив баг в очередном обновлении (на момент написания статьи версия 0.4.5), а сообщение перевёл в раздел “info”, за что ему огромное спасибо 🤝.
Если кому интересны подробности, то вот ссылки:
Спасибо, что читаете. И вновь повторюсь: админы делятся на два типа: те, кто делает бэкапы, и те, кто обязательно будет их делать. Всех благ!

