Repository navigation
Releases
Releases · Altinity/clickhouse-backup
2.8.3
v2.8.3
NEW FEATURES
- support tables with local and
plain_rewritabledisks in one storage policy: ClickHouse rejects FREEZE for them, socreatehardlinks the local parts intoshadowthe way FREEZE does and copies the parts of the plain disks from the bucket, with merges and moves of the table stopped (needsSYSTEM MERGESandSYSTEM MOVESgrants, moves are now stopped for every table on a storage policy with plain disks), plain disks inskip_disksare skipped,clickhouse.disk_mappingis applied to the part paths,sync_replicated_tables,freeze_by_part_whereandignore_not_exists_error_during_freezeapply to such tables as they apply to FREEZE, parts on object disks with local metadata (includingcacheandencryptedwrappers over them) next to plain disks are rejected, defaultrestoreof such tables requires ClickHouse 25.4+ (older versions rejectATTACH PARTfor a storage policy with plain disks, userestore_as_attachthere) - support Antalya
metadata_type=casdisks withuse_embedded_backup_restore: true,create_remoteno longer fails withdisk "cas" ... is not exists locallyandrestore_remoteno longer fails withunsupported <metadata_type>cas</metadata_type>, without embedded mode a table on a CAS disk fails with an explicit error, becauseFREEZEon CAS leaves no local files to hardlink, fix #1562
IMPROVEMENTS
rebasecopies parts, archives and object disk blobs of one table in parallel, previously everyCopyObjectinside a table ran strictly one by one andgeneral.rebase_concurrencylimited only the number of tables, now it also limits parallelCopyObjectrequests per table, so up torebase_concurrency^2copies are in flight,rebaselogs the required backups chain,rebase_concurrencyandmax_copy_objecton start andrebase table start/rebase table finishwithprogress,duration,copied_sizeandobject_disk_sizefor each table atinfolevel, fix #1648- update go dependencies: aws-sdk-go-v2, aws-sdk-go-v2/feature/s3/transfermanager 0.5.1, cloud.google.com/go/storage 1.70.0, clickhouse-go/v2 2.50.0, klauspost/pgzip 1.3.0, pkg/sftp 1.13.12, cos-go-sdk-v5 0.7.76, google.golang.org/api 0.309.0 and others
BUG FIXES
restoreandrestore_remoteno longer drop and recreate every user-defined SQL function from the backup, previously each function gotDROP FUNCTION IF EXISTS+CREATE OR REPLACE FUNCTION(function was unavailable between them) even with--tablesfor a single table, and silently rolled back definitions changed after the backup; now existing functions are read fromsystem.functionsfirst, a missing function is created withCREATE FUNCTION IF NOT EXISTS, an identical one is skipped without any query (soCREATE FUNCTION/DROP FUNCTIONgrants are not required), a changed one is kept with a warning and overwritten by atomicCREATE OR REPLACE FUNCTIONonly with--rm, the same as tables and RBAC objects; functions are restored before tables, so tables and materialized views which use UDF can be created on a clean server;restore --dry-runandrestore_remote --dry-runshowfunctionswithcreate,replace,keep_existingorskip_identicalfor each function, fix #1636- FIPS builds no longer overwrite an explicit
AWS_USE_FIPS_ENDPOINT,clickhouse-backup-fipsset it totrueunconditionally at startup, so with a customs3.endpoint(MinIO, Ceph RGW, other S3-compatible storage) every S3 call failed withS3 ResolveEndpoint: endpoint rule error, A custom endpoint cannot be combined with FIPSandAWS_USE_FIPS_ENDPOINT=falsehad no effect; it is stilltrueby default when unset, the variable only selects AWSs3-fips.*hostnames and does not affect the FIPS 140-3 crypto module s3.allow_multipart_downloadnow uses byte-range GETs by default, news3.multipart_download_type(envS3_MULTIPART_DOWNLOAD_TYPE, defaultrange):rangerequests byte ranges ofchunk_sizewithIf-Matchset to the multipart object ETag,partkeeps the previous partNumber GETs, which send the first part ETag asIf-Matchand get HTTP 412 PreconditionFailed on Ceph RGW (Hetzner Object Storage) from part 2 on (each part has its own ETag); the setting applies only when multipart download actually runs (allow_multipart_download: trueanddownload_max_bytes_per_second: 0), on other S3-compatible storage (NuObject) part-number downloads failed withunexpected EOF, fix #1632, #1655rebasewithremote_storage: azblobno longer fails withBlobNotFoundfor databases and tables whose names need encoding, such astest-dbor the.inner_id.table of a materialized view, the server-side copy source URL is escaped now, fix #1637- add
s3.timeout(envS3_TIMEOUT, default30m, empty or0disables it) as HTTP response header timeout for S3 requests including object disk connections, previously S3 had no per-request timeout andcreate_remotecould hang forever inCopyObject/UploadPartCopywhen S3 accepted the request and never answered, a timed out request is retried on a new connection;timeoutalready present in configs was silently ignored before, it accepts a duration (30m) or a number of seconds (3000), fix #1645 downloadof a regular (not embedded) backup no longer fails withChown backup disk: lstat .../<backup>: no such file or directorywhen running as root withuse_embedded_backup_restore: trueandclickhouse.embedded_backup_diskconfigured, fix #1653- fix
--link-destquoting in the rsync upload example (test/integration/rsync/upload.sh), rsync reported--link-dest arg does not existand copied every file instead of hardlinking unchanged parts, rsync calls in upload / download examples use--protect-args
2.8.2
v2.8.2
NEW FEATURES
- add
clickhouse.skip_restore_non_sharded_replicated_tables(envCLICKHOUSE_SKIP_RESTORE_NON_SHARDED_REPLICATED_TABLES, defaultfalse),clickhouse.skip_check_non_sharded_replicated_tables_cluster(envCLICKHOUSE_SKIP_CHECK_NON_SHARDED_REPLICATED_TABLES_CLUSTER, default{cluster}) andclickhouse.always_sharded_macros(envCLICKHOUSE_ALWAYS_SHARDED_MACROS, default["shard"]), when truerestore,restore_remoteandrestore --dry-runrestore the data of non-sharded replicated tables (ZooKeeper path in the backup DDL resolves to the same value on every shard, macro values are read withcluster(..., system.macros),{database},{table},{uuid}are the same for the table everywhere) only on the first replica of the first shard of the cluster fromsystem.clusters, every other node restores only their schema and gets the data through replication, so the same config works on every node, a macro listed as sharded with the same value on all shards is reported with a warning, such tables are never rebound todefault_replica_path, not supported withuse_embedded_backup_restoreandrestore_cloud, fix #1181 use_embedded_backup_restore_clusternow orchestrates the whole cluster instead of only addingON CLUSTERto the BACKUP / RESTORE SQL:create,create_remote,restore,restore_remoteon the initiator node send the same command with the new--embedded-on-cluster-workerflag to every other node of the cluster through itssystem.backup_actionstable (clickhouse-backup serverwithapi.create_integration_tables: trueis required on every node) and wait for them, a worker skips the BACKUP / RESTORE SQL and only describes, uploads, downloads and fixes its ownshards/N/replicas/M/part of the shared destination, per tablemetadata/<db>/<table>.jsonmoved undershards/N/replicas/M/next to the.sqlof each node (such backups are taggedembedded,cluster=<name>, older backups without the tag keep the flat layout),.backup,metadata.json, rbac, configs and named collections stay on the initiator,delete localis propagated to the other nodes asdelete local --embedded-on-cluster-worker, sodeleteaccepts the flag too, a single node cluster behaves as before, fix #928- add
azblob.chunk_size(envAZBLOB_CHUNK_SIZE, default0), a fixed upload block size used whenceil(file size / chunk_size) <= max_parts_count, clamped to [2MiB, 4000MiB],0keeps the automaticfile size / max_parts_countclamped to [2MiB, 10MiB], brings back the explicit size removed together withAZBLOB_BUFFER_SIZEin v2.6.6, fix #1142 - back up and restore Kafka offsets of Kafka engine tables with
kafka_keeper_path/kafka_replica_name(StorageKafka2, ClickHouse 24.8+), previously only the schema was backed up and the restored table re-read from the consumer group offset (lost rows) or fromearliest(duplicates);createdetaches such tables before FREEZE so the offsets match the frozen data of the MV target (DETACH PERMANENTLYinENGINE=Replicateddatabases), attaches them back after FREEZE (also on error and cancel) and stores the Keeper subtree without ephemeral nodes inkafka_keeperfield ofmetadata/<db>/<table>.json;restorewrites the offsets to Keeper right beforeCREATE(after the--rmDROP), when the path already exists without active replicas the offsets are overwritten with a warning, when it has active replicas they are left untouched with a warning;--restore-database-mapping/--restore-table-mappingrewritekafka_keeper_path, a mapped restore with the samekafka_group_namenext to a live source on ClickHouse < 25.6 fails before any Keeper write, because consumer group rebalance would steal the partitions of the source; betweenrestore --schemaandrestore --data(Altinity ACM restore sequence) consumption is held by persistent partition lock nodes, which the data phase removes, add--skip-kafka-keeper-holdtorestore/restore_remote(RESTskip_kafka_keeper_hold) to disable it, embedded backups only log a warning, fix #1120 list remoteandrestore_remoteauto-detect native ClickHouse Cloud backups (BACKUP ... TO S3 / AzureBlobStoragewith<name>/.backupand withoutmetadata.json) unders3.path/azblob.path, such backups are listed withdata_format: cloudand size instead ofbroken (can't stat metadata.json),clean_remote_brokenandbackups_to_keep_remoteretention no longer delete them,restore_remote <name>(CLI andPOST /backup/restore_remote/{name}) switches torestore_cloudwith--tables,--partitions,--skip-empty-tables,--rm,--dry-runandrestore_schema_on_clustersupport, options whichrestore_cloudcan't apply anddownloadfail explicitly; the base backup chain of incremental cloud backups is resolved from<base_backup>of each.backup(verified bybase_backup_uuid) and passed toRESTOREwith the configured credentials instead of the ones embedded in the manifest, a base in the same bucket and path is shown as+<base>inlist remoteand protected by thedelete remoteguard, fix #1574- add
general.rbac_restore_always,general.config_restore_always,general.named_collections_restore_always(envRBAC_RESTORE_ALWAYS,CONFIG_RESTORE_ALWAYS,NAMED_COLLECTIONS_RESTORE_ALWAYS, defaultfalse), restore-side counterparts of*_backup_always, act as--rbac/--configs/--named-collectionsforrestoreandrestore_remote(including--streaming), disabled by default because these restores overwrite existing objects and runrestart_command - add
general.download_transfer_concurrency(envDOWNLOAD_TRANSFER_CONCURRENCY, default0=download_concurrency^2, negative keeps the legacy per-table limit), one transfer budget perdownloadshared by every part, archive and required part of all tables, so a single big table can use all the connections at the tail of a download, for FTP the budget is capped by2*ftp.concurrency; addgeneral.download_table_order(envDOWNLOAD_TABLE_ORDER,largest_first(default) ormetadata), only the download dispatch order changes, restore order is not affected, fix #1591 - add
general.file_transfer_concurrency(envFILE_TRANSFER_CONCURRENCY, default1), withcompression_format: nonefiles of one data part are transferred by several streams which only take slots of the shareddownload_transfer_concurrency(download) /upload_concurrency^2(upload) budget left idle by other parts, so the total number of transfers never grows above the budget, fix #1454 - add
gcs.ranged_download_concurrency(envGCS_RANGED_DOWNLOAD_CONCURRENCY, default0= disabled),gcs.ranged_download_min_size(default 64MiB),gcs.ranged_download_chunk_size(default 32MiB) andgcs.ranged_download_max_buffers(default64), archives larger thanranged_download_min_sizeare read by concurrent byte range requests and fed to decompression in order, without a temporary file, memory is capped process-wide byranged_download_max_buffers * ranged_download_chunk_size, works withdownload_max_bytes_per_second, thanks @minguyen9988, fix #1592
IMPROVEMENTS
- GCS: all pooled clients share one cached OAuth2 token, previously each client fetched its own token, so a cold
delete remotewithgcs.delete_concurrency: 50opened about 50 parallel TLS handshakes tooauth2.googleapis.comand could fail withauth: cannot fetch token: ... TLS handshake timeout, and every retry after a delete error repeated it downloadwithclickhouse.force_rebalancespreads parts round-robin across local JBOD disks whose free space differs by less than 1% of total space (typically several disks on the same filesystem), previouslyleast_usedpicked the disk with the largest free space from statvfs noise and all parts of a download could land on a single disk- Docker image: base image changed from
alpine:3.23toalpine:latestand pre-installed packages are upgraded withapk upgrade, thealpine:3.23base shippedlibcrypto3/libssl33.5.7-r0 with CVE-2026-63073 and CVE-2026-75803, thanks @dfdemar, fix #1588 - update go dependencies: aws-sdk-go-v2, azure-sdk-for-go, cloud.google.com/go/storage 1.69.0, clickhouse-go/v2 2.49.0, urfave/cli/v3 3.14.0, golang.org/x/crypto 0.58.0, klauspost/compress 1.20.3 and others
BUG FIXES
- the SQL query log no longer prints passwords embedded in URLs:
pass=/password=query string parameters anduser:password@are written as[MASKED], withapi.create_integration_tables: trueandapi.passwordset everyserverstart logged the DDL ofsystem.backup_actions,system.backup_listandsystem.backup_versionwith the API password in plain text inside theURL()engine argument, atinfolevel with the defaultclickhouse.log_sql_queries: true, the query sent to ClickHouse is unchanged, fix #1630 s3.chunk_sizeis now also used whenceil(file size / chunk_size)is exactlys3.max_parts_count, previously it fell back tofile size / max_parts_count, ReadMe.md now shows the real default5242880and the semantics ofs3.chunk_size, raise it to upload big files as fewer bigger parts instead ofmax_parts_count=1workaround, each multipart upload allocates about(s3.concurrency + 2) * chunk_sizebytes,s3.chunk_size...
2.8.1
v2.8.1
NEW FEATURES
- add
clickhouse.drop_replica_if_exists(envCLICKHOUSE_DROP_REPLICA_IF_EXISTS, CLI--drop-replica-if-existsforrestore/restore_remote, also accepted as thedrop_replica_if_existsquery argument byPOST /backup/restoreandPOST /backup/restore_remote) — when the replica entry<replica_path>/replicas/<replica_name>of a restoredReplicated*MergeTreestill exists in ZooKeeper but no local table uses that path, executeSYSTEM DROP REPLICA '<replica_name>' FROM ZKPATH '<replica_path>'and restore the table with the ORIGINAL replication path from the backup, instead of the default behavior which silently rebinds the table toclickhouse.default_replica_path/clickhouse.default_replica_name; requires ClickHouse 20.4+, the restore fails when the drop is impossible (older server, or a local table still occupies the path), the statement is executed withoutON CLUSTERbecause the path and replica name are resolved with the macros of the current node; MUST stayfalseduring a concurrent multi-replica restore, fix #1162 - add
--streamingflag tocreate_remote,restore_remoteandwatch,--watch-streamingflag toserverand thestreamingquery argument toPOST /backup/create_remote,POST /backup/restore_remoteandPOST /backup/watch— process the backup table by table:create_remote --streamingruns freeze -> upload -> remove local copy per table,restore_remote --streamingruns download -> attach -> remove local copy per table, so only the tables in flight occupy local disk space and backups bigger than the free local space can be uploaded or restored; the local backup is not kept afterwards; interrupted runs are continued with--resumevia the dedicatedcreate_upload_streaming.state2/download_restore_streaming.state2state files (add these command names toapi.complete_resumable_after_restart_commandsto resume them after an API server restart); not available withuse_embedded_backup_restore: trueorremote_storage: custom, fix #780 - add
restore_cloudcommand andPOST /backup/restore_cloudREST API endpoint — restore a ClickHouse Cloud native S3 backup (created via external backup / Bring Your Own Backup) onto self-managed ClickHouse: reads the.backupmanifest directly from S3 (includingdata_file_name_generator: Checksumblob layout), rewritesENGINE = Shareddatabases toAtomicandShared*MergeTreetables to the matchingReplicated*MergeTree(adding--replicated-zk-path/--replicated-replicaarguments when Cloud DDL has none), applies the DDL and runsRESTORE TABLE ... FROM S3(...)/AzureBlobStorage(...)withallow_different_database_def=1, allow_different_table_def=1; source storage is S3-compatible (AWS, GCS overs3->endpoint: https://storage.googleapis.comwith HMAC keys, MinIO) or AzureBlobStorage (when--container/--azblob-restore-urlis passed orgeneral->remote_storage: azblob); supports--tablesfilter,--base-prefixfor incremental backups,--s3-restore-url/--azblob-restore-urloverrides,--skip-empty-tables,--continue-on-error,--drop,--paralleland--dry-run; note: backups containingPackedparts (data.packed) require ClickHouse 26.8+ on the target server, fix #1508 - add
--dry-runflag tocreate,create_remote,upload,download,restore,restore_remote,deletecommands and thedry-run(dry_run) query argument to the matching REST API endpoints — instead of performing the operation, report the number of tables, parts and the data size (split into local-disk and object-disk bytes, human readable in the log) which would be processed, honoring--tables,--partitions,--diff-from/--diff-from-remote,--schemaand the other filters;create --dry-runadditionally forecasts the disk space the backup hardlinks will occupy over time (bytes held in parts younger than 1/7/30 days approximate what background merges will rewrite within that horizon, up to the full local data size in the worst case) anddelete --dry-runreports the dependent incremental backups; API dry-run requests execute synchronously, are allowed even when another operation is in progress regardless ofapi.allow_parallel, and return the report as JSON in the response body; every dry-run also fills the newresultfield of its status row, so the report is visible viaGET /backup/status,GET /backup/actionsand thesystem.backup_actionstable (which gained aresult Stringcolumn, manually created tables need it added orinput_format_skip_unknown_fields=1); estimation limits:createdoesn't subtract--diff-from-remoteincrements,create_remote/uploadcan't know the compressed size before compression, parts of backups made by versions older than 2.7.0 have no per-part sizes and are counted inunknown_size_parts, fix #1012 - add
general.callback_url(envCALLBACK_URL) andgeneral.callback_timeout(envCALLBACK_TIMEOUT, default5s) — HTTP POST completion notification for API, one-shot CLI commands, and eachwatchiteration; API?callback=overrides the global URL when non-empty; the payload keeps the existingstatus/error/operation_idfields and addscommandandduration;statusis now alsocancelwhen the operation was killed via/backup/kill; callbacks are sent asynchronously, failures are logged and never change the backup result; one-shot CLI commands andwatchiterations are now registered in/backup/statuslike API operations, fix #1481 - add
general.status_history_size(envSTATUS_HISTORY_SIZE, default1000) — upper bound on how many finished operations are kept in the in-memory status list exposed by/backup/statusandsystem.backup_actions; needed becausewatchnow records one operation per iteration, so the history would otherwise grow for as long as the process lives; operations still running are never dropped, whatever their age delete local|remote <backup_name>andPOST /backup/delete/{where}/{name}now refuse to delete a backup which other backups require viarequired_backupand report the dependent backup names, instead of silently breaking the incremental backups chain (the breakage surfaced only later, when a descendant was downloaded or restored, and for object disks the descendantrequiredparts blobs were deleted together with the parent); pass--force(force=1for the API) to get the old behavior, or setgeneral.rebase_during_delete: true(envREBASE_DURING_DELETE, defaultfalse) to rebase every dependent increment first (same as therebasecommand) so the chain stays restorable — rebase copies the deleted backup parts into its dependents, so deletion time grows with the copied data size and a rebase failure aborts the delete.backups_to_keep_local/backups_to_keep_remoteretention is not affected, fix #1493restore --rbacnow converts RBAC objects betweenlocal_directoryandreplicateduser directories, when the target server has only one of the two shapes: a Keeper dump from the backup is written as<access_control_path>/<uuid>.sqlfiles when the target has noreplicateduser directory, and backup*.sqlfiles are written to Keeper (<zookeeper_path>/uuid/<uuid>plus the<zookeeper_path>/<type char>/<name>index znode) when the target has nolocal_directoryuser directory; previously the half which had no matching storage was silently lost while the restore still reported success; restoring a backup with RBAC objects onto a server which has neither user directory now fails explicitly, fix #881
IMPROVEMENTS
- support tables which declare their storage inline in the DDL via
SETTINGS disk = disk(...)instead of astorage_policy(ClickHouse 23.2+, tested from 24.8), including nestedcacheandencryptedwrappers over an object storage disk; such disks are registered by ClickHouse under a generated__tmp_internal_<hash>name (or undername = '...') and are absent frompreprocessed_configs/config.xml, socreate/upload/download/restoreused to fail with__tmp_internal_... is not present in object_disk.DisksCredentials; object storage credentials are now taken from the table DDL and the target disk is resolved again after the schema is restored, so the generated name may differ between the source and the target server. Note that the DDL stored in the backup metadata contains thesecret_access_keyin plain text, same as forS3/MySQLengine tables (#640), and thatclickhouse-backupneeds to run on the ClickHouse host (to read/var/lib/clickhouse/metadata/<db>/<table>.sql) or the server needsdisplay_secrets_in_show_and_select=1, becauseSHOW CREATE TABLEmasksdisk(...)arguments as'[HIDDEN]', fix #943 - fail fast instead of burning the whole
retries_on_failurexretries_durationbackoff budget per file when a remote object is permanently missing (S3NoSuchKey/404, GCS 404, AzureBlobNotFound, FTP/SFTP not-found) duringdownload,restore_remoteand other retried remote operations; addgeneral.allow_missing_files_on_download(envALLOW_MISSING_FILES_ON_DOWNLOAD, defaultfalse),--allow-missing-filesCLI flag fordownload/restore_remoteand theallow_missing_filesquery argument forPOST /backup/downloadandPOST /backup/restore_remote— salvage mode which skips data parts missing on remote storage with anerror-level log and a ...
2.8.0
v2.8.0
NEW FEATURES
- add
rebasecommand andPOST /backup/rebase/{name}API endpoint — copyrequiredparts from therequired_backupchain into a remote incremental backup via server-sideCopyObject(with streaming fallback) and remove therequired_backupdependency, so the incremental backup becomes a full one without re-uploading data from the ClickHouse host; per-table parallelism is controlled bygeneral.rebase_concurrency(envREBASE_CONCURRENCY, default =download_concurrency); requires backups made withupload_by_part: trueand the samecompression_formatacross the chain, fix #1344, #1444 - add
general.rebase_before_remove_old_remote(envREBASE_BEFORE_REMOVE_OLD_REMOTE, defaultfalse) — makesbackups_to_keep_remotea strict limit: when deletion of old remote backups is blocked byrequired_backuplinks from kept backups, the oldest kept increment is rebased first (same as therebasecommand) so the whole out-of-window chain becomes deletable; rebase failure is not fatal and falls back to the legacy keep-required behavior - add
--scheduletowatchcommand,general.watch_schedulesconfig section (envWATCH_SCHEDULES,;-separated) and theschedulequery argument toPOST /backup/watch— named cron-driven backup chains inname=<name>,full=<cron>[,increment=<cron>][,full_type=create|rebase][,delete_previous_cycle=true|false]format (standard 5-field cron, optional leading seconds field,@every/@dailydescriptors), can be specified multiple times, mutually exclusive with--watch-interval/--full-interval;nameis added as a prefix towatch_backup_name_templateto isolate backup chains,full_type=rebasecreates the scheduled full backup as increment +rebase(server-side copy of the previous chain instead of a full re-upload),delete_previous_cycle=truedeletes all older backups of the chain after a successful full backup, fix #1354 - add
--list-partsand--partitions(--list-partitions) flags totablescommand — additionally list every physical part (name,partition_id, size) or the distinct partitions (partition_id,partition, parts count, size) for each table; against the live server the data comes fromsystem.parts, against--local-backup/--remote-backuppart names come from backup metadata (partition_idderived from the part name), fix #959, #1333 - add
--rbac,--configs,--named-collectionsflags toserver --watch— the server command never passed them through, so watch mode backed up only data even though the standalonewatchcommand and the REST API endpoints already supported them, fix #955 - add
rebalancecommand andPOST /backup/rebalance/{name}API endpoint (with--tablesand--dry-runsupport) — move data parts inside a local backup between disks: hardlink from the live part whensystem.partsshows it on another disk; parts absent fromsystem.partsare copied to the storage-policy disk of the same type with the most free space (unreserved_space - keep_free_spacefromsystem.disks), parts on a disk which is absent or left the policy always move; parts on object disks are skipped, fix #1024 - add
gcs.allow_multipart_upload(envGCS_ALLOW_MULTIPART_UPLOAD, defaultfalse, experimental) withgcs.upload_concurrencyandgcs.multipart_upload_min_size(default 1GB) — files bigger thanmultipart_upload_min_sizeupload to GCS as multiple parallel parts (part size ischunk_size) via gRPC client and compose into the final object; not compatible withendpoint,force_http,encryption_key, fix #1028 - add
gcs.allow_multipart_download(envGCS_ALLOW_MULTIPART_DOWNLOAD, defaultfalse) withgcs.download_concurrency— download each file as parallel range reads (part size ischunk_size) into a temporary file, mirrorss3.allow_multipart_download, fix #1028 - add
general.disable_environment_override(settable ONLY in the config file, no environment variable name on purpose, defaultfalse) — whentrue, config values come only from the config file: all environment variables and the--envCLI flag are ignored during config loading; protects against accidental overrides such as Kubernetes service-discovery variables, fix #1079
IMPROVEMENTS
- write a gzip-compressed bbolt file manifest
manifest.bolt.gzduringuploadand read it duringdownload/restoreto eliminate the per-partWalk(ListObjectsV2and analogs) calls to remote storage that dominated restore time on backups with many parts; memory stays bounded even with millions of files (batched writes on upload, mmap-backed prefix seek on read), with graceful fallback toWalkwhen the manifest is missing or unreadable, and manifest failures are non-fatal for the backup, fix #1375, #1380 - store per-part
sizein table metadata duringcreateand check the required free disk space right after table metadata download, before downloading any data — the check respects--tables/--partitions, resolves sizes ofrequiredparts through the required-backup chain, and when--hardlink-exists-filesis used doesn't count parts which can be hardlinked from existing local parts (matched byhash_of_all_filesviasystem.partsor by CRC64 ofchecksums.txt), fix #1268 - fail fast on canceled context in the retrier — SIGTERM /
POST /backup/killno longer trigger a useless extra retry attempt with a misleadingWill wait and retrywarning per goroutine; during API server shutdown log which in-flight command andcancel_operation_timeoutthe server is waiting for instead of blocking silently
BUG FIXES
- fix silent truncation of compressed backup uploads to S3 when
s3.check_sum_algorithm: CRC32(Object Lock buckets): the multipart upload stopped at the declared size (sum of raw file sizes), dropping the tar headers/padding/trailer tail of the archive; the stream is now consumed until EOF, fix #1471 - fix
rbac_conflict_resolution: ignoreduringrestore --rbac— RBAC objects from the backup that already exist on the server are now really ignored: theiraccess/*.sqlfiles are not copied over the live ones and the correlated replicateduuidznodes and name-to-uuid mappings are not restored into Keeper, fix #1013
2.7.4
v2.7.4
NEW FEATURES
- add
clickhouse.parts_columns_max_bytes_before_external_group_by(envCLICKHOUSE_PARTS_COLUMNS_MAX_BYTES_BEFORE_EXTERNAL_GROUP_BY, default100000000) andclickhouse.parts_columns_max_memory_usage(envCLICKHOUSE_PARTS_COLUMNS_MAX_MEMORY_USAGE, default200000000) — make the memory caps injected into thesystem.parts_columnscheck query configurable instead of hardcoded,0means don't inject the corresponding setting (pre-2.7.2 behavior), fix #1420
BUG FIXES
- fix
--restore-database-mapping/--restore-table-mappinglosing unrelated comma-separated--tablespattern items, or applying the mapping to the wrong item when the same source database/table name was repeated across items — each comma-separated pattern item is now matched and substituted independently, fix #1421
2.7.3
v2.7.3
NEW FEATURES
- add
general.max_broken_part_ratio(envMAX_BROKEN_PART_RATIO, default0) — when >0,create/create_remoteskips broken data parts instead of aborting the whole backup as long as the broken/total part ratio stays at or below the configured fraction; skipped parts are logged as warnings and the backup is marked successful but partial;0preserves the legacy any-broken-part-fails behavior, fix #1418 - add
clickhouse.rebind_replica_path_if_exists(envCLICKHOUSE_REBIND_REPLICA_PATH_IF_EXISTS, CLI--rebind-replica-path-if-exists, also accepted by the REST APIPOST /backup/restoreendpoint) — opt-in rebind of the replicated table ZK path todefault_replica_pathduringrestorewhen the resolved path already has leftover children but no local table uses it (async-stale state after DROP); MUST stayfalseduring a concurrent multi-replica restore to avoid split-brain, fix #849 - add
gcs.disable_http2(envGCS_DISABLE_HTTP2) — force HTTP/1.1 for the GCS client to avoid single-TCP-connection HTTP/2 multiplexing becoming the bottleneck for high-throughput parallel downloads, fix #1437
IMPROVEMENTS
- switch the S3 client retry mode from
aws.RetryModeStandardtoaws.RetryModeAdaptive, so upload/download retries back off with a client-side rate limiter under throttling (SlowDown/503) instead of a fixed token bucket - support tables with
ENGINE=Aliasduringrestore— strip the explicit column list (Alias inherits columns from the target table) and addallow_experimental_alias_table_engine=1for ClickHouse 25.11+, fix #1426 - skip tables from databases with engine
DataLakeCatalog(read-only external catalogs) during backup, in addition toMySQL/PostgreSQL/MaterializedPostgreSQL, fix #1417 - speed up
createon servers with many tables: batch the in-progresssystem.mutationslookup instead of issuing one query per table (O(N^2) -> O(N)), fix #1431 - speed up
restore/downloadtable list resolution: eliminate the O(N^2) linear duplicate scan ingetTableListByPatternLocal/getTableListByPatternRemotevia a shared O(1) dedup keyed onmetadata.TableTitle, fix #1430 - extend TestFlows FIPS 140-3 test coverage: dedicated test steps, stronger
--fips-info/version-output and TLS-handshake assertions, updated requirements documentation, fix #1443
BUG FIXES
- restore the default
general.compression_use_multi_threadtotrue: #1378 defaulted it tofalse, which silently dropped gzip/zstd compression from multi-threaded (pre-1378 gzip always usedpgzip) to single-threaded and caused ~30% slower upload throughput for backups dominated by one large table (whereupload_concurrencyprovides no per-stream parallelism); the option is now silently ignored instead of failing config validation for formats other than gzip/zstd - fix v2.7.1 regression where
restore --schemaon a second replica rebound the ZK replica path even while the first replica was active and healthy — a foreign/renamed local table occupying the resolved ZK path is still detected automatically viasystem.replicasand rebound todefault_replica_path, but a non-empty ZK path without a local table now defaults to the HA-safe join and requires the new opt-inrebind_replica_path_if_exists, fix #1428 - fix inconsistent
--rbacbehavior:restore --rbacno longer returns an error when the backup contains no RBACaccessdirectory, matching thedownload --rbacbehavior, fix #1432 - inject
ON CLUSTERforLIVE VIEWandWINDOW VIEWrestored with explicit column lists whenrestore_schema_on_clusteris set —enrichQueryWithOnClustermissed these statements and the views were created only on the local replica, fix #1433 - mask sensitive values (passwords, storage credentials, encryption keys) when logging
--envconfig overrides, and in the API server action logs and/backup/statusoutput, fix #1441
2.7.2
v2.7.2
IMPROVEMENTS
- include the data part file path and source object key (
fPath,srcKey) inobject_diskCopyObject/CopyObjectStreamingerror messages duringcreateandrestore, so brokenobject_diskdata (keys missing on remote storage) points to the exact failing file instead of just the shadow/table name - document GCS Workload Identity authentication for the
gcsremote storage inExamples.md
BUG FIXES
- fix
clickhouse.skip_table_engines(envCLICKHOUSE_SKIP_TABLE_ENGINES) silently keeping some matching tables: the in-place slice removal advanced the cursor past the next element, so adjacent tables sharing a skipped engine were not all skipped; iterate in reverse so every match is dropped, fix #1416 - ensure
/backup/kill(and context cancellation in general) promptly aborts an in-flightdownload/restoreeven when a read is stalled on a slow/half-open network or disk backpressure — the source reader is now force-closed on cancellation so blockedReadcalls inDownloadCompressedStream/DownloadPath, the AzureCopyObjectpoll backoff, andobject_disk.CopyObjectStreamingreturn instead of running to completion, and the resumable.pidfile is removed, fix #1365
2.7.1
v2.7.1
NEW FEATURES
- add
--fips-infoapp-level flag — prints binary name, version, git commit, build date, Go version, and the FIPS module build/runtime state (GOFIPS140build setting,GODEBUG fips140default/runtime) then exits, without requiring a Go toolchain, fix #1402 - add Azure AD Workload Identity support for
azblob— whenAZBLOB_USE_MANAGED_IDENTITY=trueand theAZURE_TENANT_ID/AZURE_CLIENT_ID/AZURE_FEDERATED_TOKEN_FILEenv vars are injected (e.g. by the AAD Workload Identity webhook), the federated token is used to authenticate; seeExamples.mdfor deployment, fix #1124
IMPROVEMENTS
- add
general.compression_use_multi_thread(envCOMPRESSION_USE_MULTI_THREAD, defaultfalse),general.compression_threads(envCOMPRESSION_THREADS, default0= auto/GOMAXPROCS) andgeneral.compression_buffer_size(envCOMPRESSION_BUFFER_SIZE, default0) config options to tune per-stream zstd/gzip threading and the compression buffer (zstd encoder window / gzip DEFLATE window / pgzip block size); per-stream compression is now single-threaded by default to avoid CPU over-subscription, sinceupload_concurrency/download_concurrencyalready parallelize across tables, fix #1378 - migrate compression from the archived, frozen
github.com/mholt/archiver/v4 v4.0.0-alpha.8to its maintained successorgithub.com/mholt/archives v0.1.5, removing thereplacedirective pinned in archiver#428 - migrate S3 storage from the deprecated
github.com/aws/aws-sdk-go-v2/feature/s3/managertogithub.com/aws/aws-sdk-go-v2/feature/s3/transfermanager; the forced CRC32aws-chunkedtrailer that broke non-AWS S3-compatible providers is now disabled viaRequestChecksumCalculation=WhenRequired, and the obsoletes3.buffer_size/S3_BUFFER_SIZEoption was removed, fix #1409 - add buffer-size and HTTP-transport tuning for high-bandwidth (10Gbps+) S3/GCS transfers:
general.pipe_buffer_size(envPIPE_BUFFER_SIZE, default 128KB),general.download_copy_buffer_size(envDOWNLOAD_COPY_BUFFER_SIZE),gcs.upload_buffer_size(envGCS_UPLOAD_BUFFER_SIZE E870),s3.http_write_buffer_size(envS3_HTTP_WRITE_BUFFER_SIZE) ands3.http_read_buffer_size(envS3_HTTP_READ_BUFFER_SIZE), fix #1376 - replace post-hoc sleep-based bandwidth throttling with a token-bucket rate limiter wrapped around the storage
Reader/Writerinterfaces, so the configured limit is enforced continuously during transfer instead of after each chunk, fix #934, #1377 - parallelize
ALTER TABLE ... UNFREEZEafterbackup createinstead of running it inline inside each table goroutine, so an UNFREEZE no longer holds anupload_concurrencyslot and blocks the next table, fix #1381 - add
clickhouse.parts_columns_batch_size(envCLICKHOUSE_PARTS_COLUMNS_BATCH_SIZE, default25) to batch thesystem.partslookups when computinghash_of_all_files, avoidingMax query size exceededfailures on tables with very many parts, fix #1408 - resolve
requireddata parts duringrestorethe same way as duringdownload— hardlink from the required backup on local disk when present, otherwise download the part from remote storage, fix #1023 ResumeOperationsAfterRestartnow ignorescreate.state2/restore.state2files instead of failing the API server startup withunknown command; onlyuploadanddownloadare auto-resumed after a server restart, fix #1083- fail fast with a clear error instead of retrying for ~35s when a remote table metadata
.jsonis missing duringdownload(covers S3NoSuchKey, GCS 404, AzureBlobNotFound, FTP/SFTP not-found) — a missing table file is a permanent broken-backup condition, not a transient one, fix #1379 - emit a clear error on
--resume downloadwhen the local backup exists butdownload.state2is missing (so it is unknown which parts are complete), instead of crashing or silently resuming on top of partial data, fix #1383 - harden FIPS 140-3 verification: native Go
GOFIPS140=v1.0.0checks, ACVP reproducibility tests, outbound S3 TLS rejection checks and container cleanup in CI/CD, fix #1399, #1401, #1404
BUG FIXES
- don't kill
clickhouse-backup serverwithFatal/os.Exitwhen the resumable state DB can't be written or read (e.g.no space left on device); the error now propagates so the server stays alive and returns it to the API client, while the CLI exits with a non-zero code, fix #1172 - fix
backup createfailing withpart "<name>" not found in system.parts ... after FREEZEwhen a ClickHouse cache disk (e.g.s3_cache) wraps an underlying S3 object disk — prefer the underlying disk name over the cache wrapper ingetDisksFromSystemDisks, fix #1396 - fix
--hardlink-exists-filesto also match parts whosehash_of_all_filesis identical but that now live under a renamed table, fix #1398 - fix
--tablecombined with--resumeon incremental backups: recursively downloading a required backup closed the parentb.dstconnection and wiped the resumable state; the connection is now saved/restored, fix #1384 - improve detection when
clickhouse-backupruns on a host whose disks differ fromclickhouse-server, instead of silently warningdoesn't contain tables for restore, fix #1037
2.7.0
v2.7.0
NEW FEATURES
- add
clean_broken_retentionCLI command — walks top-level of remotepathandobject_disks_pathand batch-deletes (with retry) every entry that is not present in the live backup list and not matched by any--exclude=<glob>(and optionally scoped by--include=<glob>). Dry-run by default; pass--committo actually delete. Useful for cleaning up orphans left by failed retention runs, fix #1371 - add
infoCLI command for per-table backup size breakdown — shows per-table size, part count, and disk breakdown for local and remote backups, supports--tables=<db>.<table>glob filter and--format=text|json|yaml|csv|tsv, acceptsall|local|remotescope, fix #1388 - add
force_rebalanceconfig option (clickhouse.force_rebalance, envCLICKHOUSE_FORCE_REBALANCE) — distribute restored data across multiple JBOD disks under the same storage policy even when the source disk name (e.g.default) exists on the target machine, fix #1350 - switch FIPS variant from FIPS 140-2 boringssl to native Go 1.24+ FIPS 140-3 (
GODEBUG=fips140=on); embed an ACVP wrapper into the shippedclickhouse-backup-fipsbinary with dual entry points (clickhouse-backup-acvpargv0 dispatch andclickhouse-backup acvpsubcommand) and ship a tracked public-scope ACVP reproducibility flow, fix #1341, #1364, #1391, #1395 - add safety check to
restore/restore_remote: fail without--rm/--dropwhen target tables already exist and contain rows (checked viaclusterAllReplicas('{cluster}')whenrestore_schema_on_clusteris set) to avoid dangerous accidentalDROP TABLE, fix #1325 - add checksum verification during
upload --diff-from/--diff-from-remotewhen part name matches, to avoid uploading mismatched data and to detect silent corruption, fix #1307
IMPROVEMENTS
- speed up
restore_remotefrom S3 incremental chains: cache backup list and avoid redundantListObjectscalls per table (previously 8h on 280GB / 3500 tables shrinks to minutes), fix #1362, #1361 - reduce backup memory footprint for databases with thousands of tables (regression introduced in v2.6.42), fix #1360
- wrap S3 credentials with
aws.NewCredentialsCache()to avoid resolving credentials on every API call (IMDS/STS), reducing latency and throttling in IRSA + AssumeRole flows, fix #1335 - simplify
hash_of_all_filescomputation via a single post-FREEZESELECTfromsystem.partsinstead of per-file hashing — also enables--hardlinks-exists-filesto consultsystem.partschecksums during download, fix #1338 - isolate FREEZE shadow directory per backup as
/var/lib/clickhouse/shadow/backup-{uuid}so concurrent backups and cleanup-after-failure don't clobber each other's shadow data, fix #1345 - add option to skip persisting
listcalls into the API serveractionsstate — prevents unbounded growth of actions state when/backup/listis used as a monitoring endpoint during long-running backups, fix #1359 - improve
killcommand to ensure all in-flight operations really finish and to remove leftover.pidfiles, fix #1365 - document missing/incorrect concurrency defaults in
ReadMe.md(download_concurrency,s3.concurrency,cos.concurrency,sftp.concurrency,ftp.concurrency), fix #1346 - migrate integration tests to testcontainers-go for better parallelism and isolation, fix #1336
- fix the
list_durationlog field formatting inpkg/storage/general.go(was emitting raw nanoseconds), fix #1337
BUG FIXES
- fix
restore_remotefor tables using sparse-column serialization: accept empty sparse metadata files instead of treatingStorageObjectCount=0as corruption, affects ClickHouse 23.8+, fix #1372 - fix
restore_remoteaborting the entire restore when an incremental backup contains a table absent from the required full backup; the missing table is now skipped with a warning, fix #1373 - fix
object_diskbackup on S3 sources with SSE-C: handle 404 from server-sideCopyObjectby falling back to streaming and stop issuingHeadObject(returns 400) on SSE-C source objects beforeGetObject, fix #1374 - fix
--rbac-onlybackup failing with "is empty backup" when the database contains RBAC objects but no tables andallow_empty_backups=false, fix #1355 - fix nested
sshconsuming stdin from thewhile readloop in the rsync helper (usessh -n) so all backup metadata files are processed instead of only the first, fix #1368 - fix backup retention logic in the rsync helper: correct line counting, numeric comparison and arithmetic handling so old backups are properly cleaned up, fix #1369
2.6.44
v2.6.44
BUG FIXES
- fix 1356, retry batch deletion in
cleanBackupObjectDisksso transient errors (e.g. GCS 503) during retention no longer leave orphaned objects inobject_disks_path