Blog
DevOps22 min czytania

Rclone — rsync dla chmury: 40+ dostawców w jednym narzędziu

Jedno narzędzie w Go, jedna składnia i 122 obsługiwane systemy składowania — od S3 i R2 przez Google Drive po SFTP i WebDAV. Pokazujemy komendy, które realnie się liczą, kopiowanie po stronie serwera bez pobierania danych, wersjonowany backup jednym parametrem `--backup-dir`, harmonogram ograniczeń pasma i konfigurację zmiennymi środowiskowymi zamiast pliku. Do tego trzy konkretne zastosowania w stacku Laravela i lista pułapek, na czele z tym, że `sync` usuwa pliki z celu.

Prędzej czy później w każdym projekcie pojawia się to samo zadanie w kolejnym wariancie. Klient trzyma dwieście tysięcy plików na Google Drive i mają wylądować na S3. Kopie wolumenów mają iść na Backblaze B2, bo taniej. Trzeba zejść z S3 na Cloudflare R2, bo rachunek za transfer wyjściowy przestał być śmieszny. Pliki z serwera, który jest wyłączany za tydzień, trzeba przenieść gdziekolwiek.

Za każdym razem ktoś pisze ten sam skrypt: SDK dostawcy, stronicowanie listy obiektów, ponowienia, obsługa limitów, równoległość, a na końcu — jeśli jest sumienny — weryfikacja, że wszystko faktycznie doszło. Skrypt działa raz, ląduje w prywatnym repozytorium i za pół roku nikt nie pamięta, czy weryfikował sumy kontrolne.

Rclone (github.com/rclone/rclone) jest tym skryptem, napisanym raz, w Go, i szlifowanym przez dwanaście lat. Jeden plik binarny, jedna składnia i 122 obsługiwane systemy składowania — tytuł tego wpisu jest w tej kwestii wyjątkowo ostrożny. Poniżej: komendy, których używa się realnie, mechanizmy, które odróżniają to narzędzie od pętli po plikach, trzy konkretne zastosowania w naszym stacku i lista pułapek — z jedną na czele, która potrafi usunąć dane.

Stan projektu

Dane z API GitHuba na 16 września 2026:

  • 59 654 gwiazdki i 5377 forków — jedno z najpopularniejszych narzędzi wiersza poleceń w ogóle,
  • repozytorium założone 16 marca 2014, czyli dwanaście lat ciągłego rozwoju. Kod w Go, licencja MIT,
  • najnowsze wydanie v1.75.1 z 4 września 2026, przed nim v1.75.0 z 31 lipca i v1.74.4 z 8 lipca — kadencja to wydanie minor raz na miesiąc lub dwa, z poprawkami pomiędzy,
  • ostatni commit z 9 września 2026, ruch w repozytorium duży,
  • 1246 otwartych zgłoszeń — przy 122 backendach wychodzi po dziesięć na backend, co jest zrozumiałe, ale też mówi, gdzie szukać przy problemie z niszowym dostawcą.

Dwanaście lat historii przy narzędziu, któremu powierza się przenoszenie danych, jest najmocniejszym argumentem, jaki można podać. Rclone przetrwał kilka generacji API u każdego większego dostawcy.

Co obsługuje

Lista w README ma 122 pozycje i warto ją widzieć w czterech grupach, bo wtedy przestaje przerażać:

  • Składowanie obiektowe zgodne z S3 — Amazon S3, Cloudflare R2, MinIO, Ceph, Backblaze B2, Wasabi, DigitalOcean Spaces, Linode, Hetzner, IBM COS, Huawei OBS, Alibaba OSS, Scaleway, IONOS, Fastly i kilkadziesiąt dalszych. Wszystkie przez jeden backend s3 z parametrem provider,
  • Dyski i chmury konsumenckie — Google Drive, Dropbox, OneDrive, Box, iCloud Drive, MEGA, Jottacloud, Koofr, Mail.ru, pCloud, Google Photos,
  • Protokoły — FTP, SFTP, WebDAV (w tym Nextcloud i Fastmail), HTTP, SMB, HDFS, Swift, Azure Blob i Azure Files,
  • Backendy specjalnememory (do testów), Internet Archive, Cloudinary, ImageKit, oraz — o czym niżej — backendy nakładane, które nie dotykają żadnego magazynu bezpośrednio.

Instalacja

Rclone jest programem w Go i przychodzi jako jeden plik binarny — to jest cała instalacja. Na Linuksie, macOS-ie i BSD najkrócej:

sudo -v ; curl https://rclone.org/install.sh | sudo bash

Skrypt sprawdza najpierw zainstalowaną wersję i nie pobiera niczego, jeśli nie trzeba. Alternatywnie: rozpakowanie archiwum z downloads.rclone.org i skopiowanie pliku do /usr/bin, obraz Dockera rclone/rclone, albo pakiet z menedżera systemu. Do aktualizacji jest wbudowana komenda rclone selfupdate, a wydania są podpisane — projekt ma osobną stronę o weryfikacji podpisów.

Dla nas najistotniejsze jest to, co z tego wynika w kontenerze: dodanie Rclone do obrazu to jedna warstwa z jednym plikiem, bez zależności systemowych i bez środowiska uruchomieniowego. Wyjątek to mount i wtyczka wolumenów Dockera, które wymagają sterownika FUSE na hoście.

Podstawy składni

Wszystko sprowadza się do jednego wzoru:

rclone podkomenda [opcje] <parametry>

Parametrem jest ścieżka lokalna albo remote w formie nazwa:ścieżka, gdzie nazwa pochodzi z pliku konfiguracyjnego. Konfigurację zakłada się interaktywnie:

rclone config

A potem używa jak zwykłego narzędzia plikowego:

rclone ls r2:kubelek
rclone copy /var/www/storage r2:kubelek/storage
rclone sync --interactive /var/www/storage r2:kubelek/storage

Dokumentacja daje przy tym radę, którą warto wziąć dosłownie: na etapie nauki używaj flagi --interactive (-i), żeby uniknąć przypadkowej utraty danych. Rclone pyta wtedy o potwierdzenie każdej destrukcyjnej operacji.

Komendy, które realnie się liczy

Podkomend jest kilkadziesiąt, ale w codziennej pracy wraca kilka:

  • copy — kopiuje pliki nowe i zmienione, nic nie usuwa. Bezpieczna domyślna opcja,
  • sync — czyni cel identycznym ze źródłem, modyfikując wyłącznie cel. To znaczy też: usuwa z celu to, czego nie ma w źródle,
  • bisync — synchronizacja dwukierunkowa, utrzymująca dwie lokalizacje zgodne w obie strony,
  • move i moveto — przeniesienie zamiast kopii,
  • check — porównuje źródło i cel po sumach kontrolnych, nie przenosząc niczego. To jest komenda, którą uruchamia się po migracji jako dowód, że się udała,
  • ls, lsd, lsl, lsf, size — listowanie i sumowanie,
  • dedupe — interaktywne szukanie duplikatów (przydatne na Google Drive, który pozwala na dwa pliki o tej samej nazwie w jednym katalogu),
  • cleanup — porządkuje remote, gdzie to możliwe (na przykład usuwa niedokończone wysyłki wieloczęściowe na S3, za które nadal się płaci),
  • mount i serve — o nich osobno.

Jedna nieoczywista rada z dokumentacji: do kopiowania pojedynczych plików używaj copy, nie sync. Efekt jest praktycznie ten sam, ale copy zużywa znacznie mniej pamięci.

Integralność jest wbudowana i to jest różnica względem własnego skryptu: sumy MD5 i SHA-1 są sprawdzane zawsze, a znaczniki czasu plików zachowywane. Przy backendach, które hashy nie udostępniają, weryfikacja spada do rozmiaru i czasu modyfikacji — i to jest jedna z rzeczy, które warto sprawdzić w tabeli funkcji opcjonalnych przed wyborem dostawcy.

Kopiowanie po stronie serwera

Mechanizm, który zmienia charakter narzędzia. Gdy źródło i cel są tym samym remote, Rclone nie pobiera i nie wysyła plików ponownie — instruuje serwer, żeby skopiował je u siebie:

rclone copy s3:oldbucket s3:newbucket

Przy stu gigabajtach to różnica między kilkoma minutami a kilkoma godzinami, a przy dostawcy liczącym za transfer wyjściowy — także różnica w rachunku. Trzy warunki i jedno zastrzeżenie: nazwy remote muszą być identyczne, backend musi to wspierać (nie wszystkie wspierają — jest tabela funkcji opcjonalnych), a operacje po stronie serwera widać w logu przy fladze -v. Komenda move też z tego korzysta, wykonując kopię po stronie serwera i usunięcie, gdy backend nie ma natywnego przenoszenia.

Dokumentacja pokazuje na tym wzorzec, który jest gotowym rozwiązaniem problemu „chcę mieć wczorajszą wersję backupu":

rclone sync --interactive remote:current-backup remote:previous-backup
rclone sync --interactive /path/to/files remote:current-backup

Pierwsze polecenie przesuwa bieżący backup na pozycję poprzedniego — bez pobierania czegokolwiek. Drugie tworzy nowy bieżący. Dwie linijki i mamy rotację, która u dostawcy kosztuje tyle, ile żądania API.

Wersjonowany backup jednym parametrem

To jest funkcja, którą warto znać, nawet jeśli poza tym Rclone nas nie interesuje. Flaga --backup-dir sprawia, że przy sync, copy i move pliki, które zostałyby nadpisane albo usunięte, są przenoszone do wskazanego katalogu z zachowaniem oryginalnej hierarchii:

rclone sync /var/www/storage r2:kopie/current \
  --backup-dir r2:kopie/old \
  --suffix "-$(date +%F)"

Efekt: current zawiera stan aktualny, a old — wszystko, co zostało po drodze zastąpione, z datą w nazwie. Zwykły sync zamienia się w backup wersjonowany, bez włączania wersjonowania po stronie kubełka i bez płacenia za nie.

Trzy warunki, o których dokumentacja mówi wprost: remote musi wspierać przenoszenie albo kopiowanie po stronie serwera; katalog backupu musi być na tym samym remote co cel; i nie może nachodzić na katalog docelowy, o ile nie wykluczymy go filtrem. To ostatnie jest klasycznym sposobem na zapętlenie synchronizacji.

Filtry i ich pułapki

Zakres operacji zawęża się filtrami — po wzorcach ścieżek, po listach plików, po wieku i po rozmiarze:

# tylko obrazy, bez katalogu cache
rclone copy /var/www/storage r2:kubelek \
  --filter "+ *.jpg" --filter "+ *.png" --filter "- **"

# tylko pliki z ostatnich dwóch dni, mniejsze niż gigabajt
rclone copy /var/log r2:logi --max-age 2d --max-size 1G

# lista plików z pliku
rclone copy --files-from lista.txt /src r2:kubelek

Dokumentacja zbiera tu ostrzeżenia, które warto przeczytać przed pierwszym użyciem, bo każde z nich odpowiada realnemu zgłoszeniu na forum projektu:

  • Nie mieszaj dwóch z trzech rodzin flag--include..., --exclude... i --filter.... Dokumentacja mówi to wielkimi literami: wyniki mogą być inne, niż się oczekuje. Zamiast tego używaj wyłącznie --filter,
  • purge nie respektuje filtrów. W ogóle. To komenda, która usuwa ścieżkę z całą zawartością,
  • Wzorce nie działają w specyfikacji remote. rclone copy "remote:dir*.jpg" /cel nie filtruje niczego; filtruje rclone copy remote:dir /cel --include "*.jpg",
  • Testuj filtry na rclone ls albo z --dry-run i -vv, zanim puścisz je na komendzie, która coś zmienia.

Trzy najczęstsze pomyłki, które dokumentacja wymienia jako najczęstsze pytania z forum: ścieżki nie podane względem korzenia remote, brak wiodącego /, gdy chcemy dopasować od korzenia, i brak **, gdy chcemy dopasować zawartość katalogu, a nie sam katalog.

Pasmo, równoległość i harmonogram

Kopiowanie stu tysięcy plików bez ograniczeń potrafi wysycić łącze albo wpaść w limity dostawcy. Trzy flagi rozwiązują to w całości:

rclone copy /dane r2:kubelek \
  --transfers 8 \
  --checkers 16 \
  --bwlimit 10M \
  --progress

--transfers to liczba równoległych przesyłów, --checkers — liczba równoległych sprawdzeń, czy plik wymaga przesłania. --progress (albo -P) daje pasek postępu, którego przy dużych operacjach po prostu się chce.

Uwaga na jednostkę --bwlimit: to bajty na sekundę, nie bity. Dokumentacja podkreśla to wytłuszczeniem i nie bez powodu — pomyłka o czynnik osiem jest tu klasykiem. Limity da się rozdzielić na wysyłanie i pobieranie (--bwlimit 10M:100k), a każdy z nich wyłączyć słowem off.

Najciekawsza jest jednak możliwość podania harmonogramu zamiast jednej wartości:

--bwlimit "08:00,512k 12:00,10M 13:00,512k 18:00,30M 23:00,off"

Czyta się to tak: od ósmej rano pół megabajta na sekundę, w porze lunchu dziesięć, po lunchu znowu pół, od osiemnastej trzydzieści, a od dwudziestej trzeciej bez ograniczeń. Można dodać dzień tygodnia. Backup, który nie przeszkadza w pracy i nadrabia w nocy, jest więc kwestią jednego parametru, a nie osobnej logiki w skrypcie.

Drobiazg, który dokumentacja wyróżnia notką: w środowiskach takich jak Docker używaj średników zamiast spacji jako separatorów wpisów harmonogramu, żeby uniknąć problemów z parsowaniem.

Cztery flagi do dużych transferów

Przy przenoszeniu setek tysięcy plików wchodzą do gry decyzje, których przy dziesięciu plikach nie widać:

  • --checksum (-c) — porównuje pliki po sumie kontrolnej i rozmiarze, zamiast po czasie modyfikacji i rozmiarze. Niezbędne, gdy backend nie potrafi ustawić czasu modyfikacji, i bardzo przydatne między dwoma remote'ami przechowującymi ten sam typ hasha,
  • --fast-list — wymusza strategię listowania ListR, pobierającą całą zawartość katalogu w jednej lub kilku transakcjach zamiast katalog po katalogu. Reguła kciuka z dokumentacji jest jasna: jeśli płacisz za transakcje i całe listowanie zmieści się w pamięci — użyj; jeśli synchronizacja jest bardzo duża — nie używaj, bo skończy się brakiem pamięci. W razie wątpliwości zostaw domyślne i pozwól narzędziu wybrać,
  • --check-first — wykonuje wszystkie sprawdzenia przed rozpoczęciem jakiegokolwiek transferu. Przydatne na systemach ograniczonych wejściem-wyjściem, gdzie transfery przeszkadzają sprawdzaniu,
  • --error-on-no-transfer — zwraca kod wyjścia 9, gdy nic nie zostało przesłane. To flaga dla skryptów: pozwala uruchomić następny krok tylko wtedy, gdy dane faktycznie się zmieniły. Dokumentacja ostrzega przy tym, że zamienia sytuację zwykle niegroźną w potencjalnie błędną — trzeba więc dostosować logikę skryptu.

Do tego --log-file i --stats 30s albo --stats-one-line, jeśli operacja ma zostawić ślad w logach zamiast na terminalu, którego nikt nie obserwuje.

Bez pliku konfiguracyjnego

Interaktywne rclone config jest wygodne na laptopie i bezużyteczne w kontenerze albo w pipeline'ie. Są dwie drogi obejścia i obie są dobre.

Zmienne środowiskowe. Nazwa powstaje ze wzoru RCLONE_CONFIG_ + nazwa remote + _ + nazwa opcji, wielkimi literami:

export RCLONE_CONFIG_MYS3_TYPE=s3
export RCLONE_CONFIG_MYS3_ACCESS_KEY_ID=XXX
export RCLONE_CONFIG_MYS3_SECRET_ACCESS_KEY=XXX

rclone lsd mys3:

Zmienna z sufiksem _TYPE jest przy tym obowiązkowa — bez niej remote nie powstanie. Ciekawostka: remote utworzony zmiennymi jest nieczuły na wielkość liter (w przeciwieństwie do zwykłych), więc mys3: i MYS3: działają identycznie. To jest właściwy sposób podawania sekretów w CI i w kontenerach: żaden rclone.conf nie musi wchodzić do obrazu.

Connection strings. Cała konfiguracja w argumencie:

rclone copy ":s3,provider=Minio,endpoint='https://minio.example.com':kubelek/dane" /tmp/dane
rclone copy :sftp,host=example.com:path/to/dir /tmp/dir

Ale prawdziwa zaleta tej składni jest inna i warto ją zrozumieć, bo rozwiązuje realny problem. Flagi backendu podane zwyczajnie stosują się do obu stron polecenia. Dokumentacja daje kanoniczny przykład: próba skopiowania pliku udostępnionego na Google Drive na własny dysk nie działa, bo --drive-shared-with-me stosuje się i do źródła, i do celu:

# nie działa
rclone copy --drive-shared-with-me gdrive:shared-file.txt gdrive:

# działa
rclone copy "gdrive,shared_with_me:shared-file.txt" gdrive:

Connection string dotyczy wyłącznie tego remote, przy którym stoi. Jedno ograniczenie: parametry nie przechodzą przez warstwy nakładane — przy remote typu crypt zbudowanym na Google Drive parametr shared_with_me zostanie zignorowany, bo backend crypt go nie zna.

Backendy nakładane

Element architektury, który zasługuje na uznanie: część backendów nie dotyka żadnego magazynu, tylko owija inny backend, dodając mu funkcję. Składają się przy tym w wiele warstw.

  • crypt — szyfrowanie po stronie klienta. Dane są szyfrowane przed wysłaniem i odszyfrowywane po pobraniu, lokalnie, więc w magazynie leżą zaszyfrowane. Dzięki temu można trzymać dane u dostawcy, któremu nie ufamy,
  • chunker — dzielenie dużych plików na części, dla backendów z limitem rozmiaru pojedynczego obiektu,
  • compress — przezroczysta kompresja,
  • alias, union, combine, hasher — odpowiednio: skrót do podścieżki, scalenie kilku remote'ów w jeden widok i dokładanie sum kontrolnych backendowi, który ich nie ma.

Przy crypt trzeba znać dwie rzeczy, i to są rzeczy poważne. Pierwsza: hasło jest przechowywane w pliku konfiguracyjnym „w lekko zaciemnionej formie" — sformułowanie jest z dokumentacji i oznacza dokładnie tyle, ile mówi. Jeśli plik konfiguracyjny nie jest bezpieczny, trzeba włączyć szyfrowanie konfiguracji. Druga: szyfrowanie działa tylko przez rclone. Dostęp do owijanego magazynu inną aplikacją da pliki zaszyfrowane i nieczytelne, a co ważniejsze — plik wgrany tam inną aplikacją nie zostanie zaszyfrowany. Do weryfikacji integralności zaszyfrowanego remote jest osobna komenda rclone cryptcheck.

Synchronizacja dwukierunkowa

Komenda bisync utrzymuje dwie lokalizacje zgodne w obie strony — zmiany z każdej strony propagują się na drugą. Dostępna od wersji 1.58 i opatrzona w dokumentacji ostrzeżeniem, które trzeba zacytować, bo jest jednoznaczne: to komenda zaawansowana, a użycie jej bez przeczytania całej instrukcji może skończyć się utratą danych.

Sposób pracy jest specyficzny i wynika z tego, że narzędzie musi pamiętać poprzedni stan obu stron. Bisync trzyma katalog roboczy z listami plików (na Linuksie ~/.cache/rclone/bisync) i porównuje aktualny stan z zapisanym, żeby odróżnić „plik dodany po jednej stronie" od „plik usunięty po drugiej". Stąd wynika procedura startu:

# pierwsze uruchomienie: budowa stanu początkowego
rclone bisync remote1:sciezka1 remote2:sciezka2 \
  --compare size,modtime,checksum \
  --resilient --resync --dry-run -MvP

# jeśli wynik wygląda dobrze — bez --dry-run
# a każde kolejne uruchomienie — TAKŻE bez --resync

Flagę --resync zdejmuje się po pierwszym przebiegu i dokumentacja podkreśla to wykrzyknikiem. Zostawienie jej oznacza, że przy każdym uruchomieniu jedna strona wygrywa arbitralnie (domyślnie Path1), co niweczy cały sens synchronizacji dwukierunkowej.

Cztery mechanizmy bezpieczeństwa warto włączyć od razu:

  • --max-delete PERCENT — kontrola bezpieczeństwa przerywająca przebieg, gdy odsetek usuwanych plików przekroczy próg. Domyślnie 50%, co jest wartością hojną; przy stabilnym zbiorze danych warto ją mocno obniżyć,
  • --check-access — sprawdza dostępność obu stron, zanim cokolwiek się zmieni. Chroni przed scenariuszem, w którym niezamontowany katalog wygląda jak katalog, z którego usunięto wszystko,
  • --resilient — pozwala kolejnym przebiegom ponowić próbę po mniej poważnych błędach, zamiast wymagać ponownego --resync. Osobno jest --recover dla automatycznego wyjścia z przerwanego przebiegu,
  • pliki blokad — bisync utrzymuje je sam, dzięki czemu może bezpiecznie działać w równoległych zadaniach crona. To nieoczywista, ale bardzo praktyczna właściwość.

Rclone jako sterownik wolumenów Dockera

Element, o którym mało kto wie, a który rozwiązuje realny problem: Rclone potrafi działać jako wtyczka wolumenów Dockera (od wersji 1.56). Kontener dostaje wtedy nazwany wolumen, którego zawartość leży w chmurze — i, w przeciwieństwie do wolumenów lokalnych, dane nie znikają przy usunięciu wolumenu.

sudo apt-get -y install fuse3
sudo mkdir -p /var/lib/docker-plugins/rclone/config
sudo mkdir -p /var/lib/docker-plugins/rclone/cache

docker plugin install rclone/docker-volume-rclone:amd64 \
  args="-v" --alias rclone --grant-all-permissions

Potem wolumen jest zwykłym wolumenem, opisanym w pliku Compose:

volumes:
  configdata:
    driver: rclone
    driver_opts:
      remote: 'gdrive:heimdall'
      allow_other: 'true'
      vfs_cache_mode: full
      poll_interval: 0

Wtyczka musi działać na tym samym hoście co demon Dockera — a w trybie Swarm na każdym węźle klastra — i wymaga sterownika FUSE. Dla backendów konfigurowalnych w całości parametrami statycznymi (SFTP, S3) plik rclone.conf nie jest nawet potrzebny: wystarczy podać opcje w docker volume create. Dla backendów z tokenami OAuth (Google Drive) trzeba wygenerować konfigurację na maszynie z przeglądarką i rozłożyć plik na węzły, bo wtyczka zarządzana nie ma czym otworzyć okna zgody.

Ocena praktyczna: to jest elegancki sposób na dane, które muszą przeżyć kontener i nie są wydajnościowo krytyczne — konfiguracja, zasoby statyczne, katalogi wymiany. Nie jest to sposób na wolumen bazy danych, bo pod spodem nadal jest FUSE nad storage'em o wysokiej latencji.

Montowanie i udostępnianie

Dwie komendy, które wychodzą poza kopiowanie. rclone mount montuje remote jako katalog przez FUSE — kubełek S3 zaczyna wyglądać jak zwykły katalog dla dowolnego programu. Wygodne i warte ostrożności: latencja storage'u obiektowego nie znika przez to, że schowaliśmy go za punktem montowania, a aplikacja intensywnie czytająca małe pliki odczuje to natychmiast.

rclone serve działa w drugą stronę: udostępnia pliki lokalne albo zdalne po HTTP, WebDAV, FTP, SFTP albo DLNA. To jest najprostsza znana nam odpowiedź na problem „aplikacja umie tylko WebDAV, a dane są na S3" — jedno polecenie i most gotowy. Do tego dochodzą wielowątkowe pobierania na dysk lokalny.

W stacku Laravela

Trzy zastosowania, które wracają w naszej pracy regularnie.

1. Migracja dysku między dostawcami

Aplikacja trzymała pliki lokalnie albo na S3, a mają być na R2. Sam transfer to jedno polecenie, ale liczy się to, co po nim:

rclone copy --progress --transfers 16 \
  /var/www/storage/app/public r2:kubelek/public

# dowód, że migracja się udała
rclone check /var/www/storage/app/public r2:kubelek/public

Dwie rzeczy do przemyślenia po stronie aplikacji. Ścieżki muszą zostać identyczne, bo w bazie leżą względne ścieżki plików zapisane przez Storage::put() — przesunięcie o jeden poziom katalogu oznacza setki martwych odnośników. I rclone check nie jest opcjonalne: to jedyny moment, w którym weryfikacja kosztuje kilka minut, a nie tydzień odzyskiwania.

2. Kopia do drugiego dostawcy

Wersjonowanie kubełka u jednego dostawcy chroni przed pomyłkowym nadpisaniem pliku. Nie chroni przed zablokowaniem konta, pomyłką w polityce dostępu ani przed przypadkowym rclone purge na produkcyjnym kubełku. Kopia u drugiego dostawcy jest tanim zabezpieczeniem przed klasą problemów, których wersjonowanie nie łapie:

rclone sync r2:produkcja b2:kopia-produkcji \
  --backup-dir b2:kopia-produkcji-stare \
  --suffix "-$(date +%F)" \
  --bwlimit "07:00,2M 22:00,off" \
  --log-file /var/log/rclone-backup.log

Harmonogram pasma trzyma to poza godzinami pracy, --backup-dir z sufiksem daje historię, a plik logu — dowód wykonania. Zadanie idzie do crona na hoście albo do harmonogramu Laravela.

3. Wywołanie z aplikacji

Rclone da się uruchomić z Laravela, ale trzeba to zrobić właściwie. Transfer plików jest operacją długotrwałą i nie ma prawa działać w cyklu żądania HTTP:

<?php

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Process;

class SyncMediaToBackup implements ShouldQueue
{
    use Queueable;

    public int $timeout = 3600;

    public function handle(): void
    {
        $result = Process::timeout(3600)
            ->env([
                'RCLONE_CONFIG_BACKUP_TYPE' => 'b2',
                'RCLONE_CONFIG_BACKUP_ACCOUNT' => config('services.b2.account'),
                'RCLONE_CONFIG_BACKUP_KEY' => config('services.b2.key'),
            ])
            ->run([
                'rclone', 'sync',
                storage_path('app/public'),
                'backup:media',
                '--stats-one-line',
                '--stats', '30s',
            ]);

        if (! $result->successful()) {
            throw new \RuntimeException("rclone: {$result->errorOutput()}");
        }
    }
}

Cztery decyzje w tym kodzie są celowe: poświadczenia idą zmiennymi środowiskowymi, a nie plikiem w obrazie; timeout jest ustawiony jawnie i na zadaniu, i na procesie; polecenie przekazujemy jako tablicę, więc nie ma powłoki do wstrzyknięcia; a niezerowy kod wyjścia rzuca wyjątek, żeby zadanie trafiło do nieudanych, a nie zniknęło.

Pułapki

  • sync usuwa. To nie jest „kopiuj nowsze" — to „uczyń cel identycznym ze źródłem", w tym usuń z celu wszystko, czego w źródle nie ma. Odwrócona kolejność argumentów przy pustym katalogu źródłowym czyści cel. Pierwsze uruchomienie zawsze z --dry-run, a na etapie nauki z -i,
  • --bwlimit jest w bajtach, nie w bitach. Pomyłka o czynnik osiem w obie strony,
  • Nie mieszaj --include, --exclude i --filter — używaj samego --filter. A purge ignoruje filtry całkowicie,
  • Nie każdy backend wspiera wszystko. Kopiowanie po stronie serwera, sumy kontrolne, znaczniki czasu, metadane — to wszystko jest opcjonalne i różni się między dostawcami. Bez hasha weryfikacja opiera się na rozmiarze i czasie, co jest słabszą gwarancją,
  • Connection string nie przechodzi przez warstwy nakładane, więc parametry backendu bazowego przy remote typu crypt są ignorowane,
  • Hasło crypt w pliku konfiguracyjnym jest tylko „lekko zaciemnione". Przy danych, które mają być naprawdę chronione, włącz szyfrowanie konfiguracji,
  • Crypt szyfruje tylko to, co przechodzi przez rclone. Plik wgrany do owijanego magazynu inną drogą zostanie tam jawny,
  • mount nie usuwa latencji. Punkt montowania na storage'u obiektowym nie zamieni go w dysk lokalny,
  • 1246 otwartych zgłoszeń przy 122 backendach — przy niszowym dostawcy warto poszukać jego nazwy w zgłoszeniach przed wpisaniem narzędzia w procedurę produkcyjną.

Podsumowanie

Rclone jest jednym z tych narzędzi, których obecność w systemie zdejmuje z zespołu całą kategorię pracy. Zamiast skryptu na każdą migrację i każdego dostawcę mamy jedną składnię, wbudowaną weryfikację sum kontrolnych i dwanaście lat cudzego debugowania cudzych API. Co z tego wynika:

  • Projekt jest wyjątkowo dojrzały — 59 654 gwiazdki, pierwszy commit w marcu 2014, wydanie v1.75.1 z 4 września 2026, kadencja co miesiąc lub dwa,
  • 122 obsługiwane systemy składowania, z czego kilkadziesiąt przez jeden backend s3 z parametrem provider,
  • Zacznij od copy, nie od sync, i pierwsze uruchomienie zawsze puść z --dry-run,
  • rclone check po każdej migracji — to jedyny dowód, że wszystko doszło,
  • Kopiowanie po stronie serwera działa, gdy nazwy remote są identyczne, i zamienia rotację backupów w dwie linijki bez transferu danych,
  • --backup-dir z --suffix $(date +%F) zamienia sync w backup wersjonowany, bez wersjonowania kubełka. Pamiętaj, że katalog backupu musi być na tym samym remote i nie może nachodzić na cel,
  • Filtruj tylko przez --filter, testuj na rclone ls albo z --dry-run -vv,
  • Harmonogram --bwlimit pozwala backupom nie przeszkadzać w godzinach pracy; w Dockerze rozdzielaj wpisy średnikami,
  • W CI i w kontenerach konfiguruj zmiennymi RCLONE_CONFIG_*, z obowiązkowym _TYPE — żaden plik z sekretami nie musi wchodzić do obrazu,
  • Z Laravela wywołuj przez kolejkę, z jawnym timeoutem, poleceniem podanym jako tablica i wyjątkiem na niezerowym kodzie wyjścia,
  • bisync traktuj z ostrożnością--resync tylko przy pierwszym przebiegu, do tego --max-delete obniżone z domyślnych 50% i --check-access dla bezpieczeństwa,
  • Wtyczka wolumenów Dockera nadaje się do konfiguracji i zasobów statycznych, nie do wolumenu bazy danych,
  • Kopia u drugiego dostawcy chroni przed klasą problemów, których wersjonowanie kubełka nie łapie.

Licencja: Rclone jest rozpowszechniany na licencji MIT — najbardziej permisywnej z powszechnie używanych, bez copyleftu i bez warunków przy komercyjnym użyciu poza zachowaniem noty o prawach autorskich. Wolno go używać w audytach i migracjach dla klientów, dołączać do własnych obrazów Dockera, wbudowywać w komercyjne narzędzia i sprzedawać usługi na nim oparte, bez obowiązku publikowania czegokolwiek. W praktyce oznacza to, że jedyną decyzją do podjęcia jest techniczna: czy plik binarny w obrazie i jedna linijka w zadaniu kolejkowym są tańsze od własnego skryptu z SDK dostawcy. Zwykle są.