Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# SqlPackage, for the Azure SQL Database adapter.
#
# Built in its own stage so the .NET SDK never reaches the runtime image. The tool
# is published as portable IL under tools/<tfm>/any, so this produces the correct
# architecture automatically: buildx runs this stage once per target platform.
# Verified on linux/arm64 as well as linux/amd64 - Microsoft's standalone zip is
# x64-only, which is why the dotnet tool is used instead of the download.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS sqlpackage-build
RUN dotnet tool install --tool-path /tmp/sqlpkg microsoft.sqlpackage && \
PAYLOAD="$(find /tmp/sqlpkg/.store -type d -path '*/tools/net10.0/any' | head -1)" && \
test -n "$PAYLOAD" && \
mkdir -p /opt/sqlpackage && \
cp -a "$PAYLOAD"/. /opt/sqlpackage/

# Base Image: Node.js 24 on Debian Slim (bookworm)
FROM node:24-slim AS base

Expand Down Expand Up @@ -98,6 +112,26 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
ldconfig && \
rm -rf /tmp/firebird.tar.gz /tmp/firebird-extract

# Step 6: SqlPackage runtime, for the Azure SQL Database adapter.
#
# Only the .NET runtime, never the SDK - the tool itself came from the stage above.
# libicu is a hard dependency: without it .NET aborts at startup with a globalization
# error that says nothing about the missing package.
#
# The wrapper exists because the adapter resolves the binary with host.which("sqlpackage"),
# and the apphost shim from a --tool-path install is not on PATH here.
RUN apt-get update && apt-get install -y --no-install-recommends libicu72 && \
rm -rf /var/lib/apt/lists/* && \
curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh && \
bash /tmp/dotnet-install.sh --channel 10.0 --runtime dotnet --install-dir /usr/share/dotnet --no-path && \
rm /tmp/dotnet-install.sh

COPY --from=sqlpackage-build /opt/sqlpackage /opt/sqlpackage

RUN printf '#!/bin/sh\nexec /usr/share/dotnet/dotnet /opt/sqlpackage/sqlpackage.dll "$@"\n' > /usr/local/bin/sqlpackage && \
chmod +x /usr/local/bin/sqlpackage && \
sqlpackage /version

# Enable corepack for pnpm support and symlink PostgreSQL 18 binaries
# On Debian with PGDG, pg binaries live under /usr/lib/postgresql/18/bin/
RUN corepack enable && corepack prepare pnpm@10.29.3 --activate && \
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<img src="https://img.shields.io/badge/Redis-DC382D?logo=redis&logoColor=white" alt="Redis">
<img src="https://img.shields.io/badge/Valkey-4B6BFB?logoColor=white" alt="Valkey">
<img src="https://custom-icon-badges.demolab.com/badge/Microsoft%20SQL%20Server-CC2927?logo=mssqlserver-white&logoColor=white" alt="MSSQL">
<img src="https://img.shields.io/badge/Azure%20SQL-0078D4?logo=microsoftazure&logoColor=white" alt="Azure SQL Database">
<img src="https://img.shields.io/badge/Firebird-F62817?logo=firebird&logoColor=white" alt="Firebird">
<br>
<img src="https://img.shields.io/badge/license-GPL--3.0-blue.svg" alt="License">
Expand Down Expand Up @@ -61,7 +62,7 @@ That promise shapes the architecture: incremental backups store whole changed fi

### 🗄️ Database Backup

- **9 Database Engines** - MySQL, MariaDB, PostgreSQL, MongoDB, SQLite, Redis, Valkey, Microsoft SQL Server, and Firebird (beta)
- **10 Database Engines** - MySQL, MariaDB, PostgreSQL, MongoDB, SQLite, Redis, Valkey, Microsoft SQL Server, Azure SQL Database (beta), and Firebird (beta)
- **Selective Database Backup** - Choose exactly which databases to back up per job instead of creating separate sources for each database
- **Multi-Database Jobs** - Back up multiple databases from a single source in one job with a unified TAR archive format
- **AES-256-GCM Encryption** - Encrypt backups with managed Encryption Profiles, key rotation, and downloadable Recovery Kits for offline decryption
Expand Down Expand Up @@ -185,6 +186,7 @@ Open [https://localhost:3000](https://localhost:3000) and create your admin acco
| Valkey | 7.2+ | Direct, SSH | Guided |
| SQLite | 3.x | Local, SSH | Yes |
| Microsoft SQL Server | 2017, 2019, 2022, Azure SQL Edge | Direct, SSH | Yes |
| Azure SQL Database (beta) | Single database, elastic pool | Direct | Yes (drops the target first) |
| Firebird (beta) | 3.x, 4.x, 5.x | Direct, SSH | Yes (pre-configured aliases) |

## 📁 Directory Sources
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ services:
# - OIDC_AUTO_REDIRECT=authentik-742 # Optional: SSO provider ID to redirect to instead of the login page
volumes:
- ./data:/data # All persistent data (db, storage, certs)
- ./backups:/backups # Optional: used for local backups
- ./backups:/backups # Optional: used for local backups
# - ./tmp:/tmp # Recommended: staging space for running backups, keeps them off the Docker disk
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export default defineConfig({
{ text: 'Valkey', link: '/user-guide/sources/valkey' },
{ text: 'SQLite', link: '/user-guide/sources/sqlite' },
{ text: 'Microsoft SQL Server', link: '/user-guide/sources/mssql' },
{ text: 'Azure SQL Database', link: '/user-guide/sources/azure-sql' },
{ text: 'Firebird', link: '/user-guide/sources/firebird' }
]
},
Expand Down
58 changes: 58 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,64 @@

All notable changes to DBackup are documented here.

## vNEXT
*Release: In Progress*

> ⚠️ **Breaking:** Retention now decides how old a backup is from the creation time DBackup recorded in its `.meta.json`, not from the file's modification time on the destination. Where the two still agree, which is the normal case, the same backups are kept as before and nothing needs doing. Where they were pulled apart, by moving a destination or copying it without preserving timestamps, retention keeps a different set from the next run onwards. That is the intended fix, because a reset modification time collapses the whole history into a single bucket and costs almost all of it, but it does mean the first run after updating can delete backups the run before it kept. Open the retention step of that first run and look for lines naming a backup whose recorded time and modification time disagree. Lock anything you cannot lose before a destination is moved.

### ✨ Features

- **retention**: Smart (GFS) policies can now keep an hourly tier next to daily, weekly, monthly and yearly. The field stays hidden behind **Add hourly tier** until it is needed, so existing policies keep their behaviour unchanged.
- **azure-sql**: New **Azure SQL Database** source type in beta, backing up through a BACPAC export.

### 🐛 Bug Fixes

- **settings**: The System Timezone now accepts renamed zones such as **Asia/Kolkata** and **Europe/Kyiv**, which were refused with **Invalid IANA timezone** in browsers that offer the modern name. Around 140 zones were affected, and the picker now also keeps the stored zone selectable in browsers that only know its legacy name. ([#147](https://github.com/Skyfay/DBackup/issues/147))
- **retention**: A policy whose mode carries no settings, such as **Smart** with no tiers stored, now keeps every backup instead of deleting all of them. Only configurations written through the API could reach this state.
- **mssql**: Azure SQL Database and Azure SQL Managed Instance are now refused up front with a message naming the product, instead of connecting successfully and then failing partway through a backup with a raw T-SQL error. Both are also named correctly in the connection test, where they previously showed as **SQL**.
- **mssql**: The Database Explorer now lists databases on servers that do not expose **sys.master_files**, showing names and table counts without sizes rather than failing the whole page with **Connection Failed**.
- **mssql**: Restoring a database under a different name now places its files in the instance's own default data and log directories, which is what makes such a restore work against a SQL Server running on Windows. A database holding more than one data file no longer has all of them moved onto the same file. ([#148](https://github.com/Skyfay/DBackup/issues/148))
- **s3**: Listing a bucket now returns every object instead of stopping at the first 1000. Because the cut fell alphabetically and backup names carry timestamps, the newest backups were the ones missing from retention, integrity checks, the destination browser and the dashboard.
- **s3**: Empty files now appear in listings on S3, Cloudflare R2, Hetzner Object Storage and S3-compatible providers. They were dropped along with folder markers, which left them out of directory backups and made them read as deleted everywhere a listing decides what still exists.

### 🔄 Changed

- **retention**: Backups are now sorted into their hourly, daily, weekly, monthly and yearly buckets by the creation time DBackup recorded when it wrote them. The file's modification time on the destination is only used for backups that have no recorded time.

### 🎨 Improvements

- **retention**: Retention policies are now validated before they are saved. A negative, fractional or non numeric tier is rejected instead of stored.
- **retention**: Reading backup metadata at the end of a job now runs up to 8 requests at once on S3, WebDAV, Dropbox, Google Drive, OneDrive and local destinations, and skips backups the listing already shows have no metadata. FTP, SMB, SFTP and rsync stay sequential because each read there costs a connection or a process.
- **retention**: The run log now names any backup whose recorded creation time disagrees with the destination's modification time by more than an hour, and reports how many backups supplied their own time.
- **s3**: Backups now upload to S3, Cloudflare R2, Hetzner Object Storage and S3-compatible providers in 8 parallel parts of 8 MB instead of the AWS SDK's 4 parts of 5 MB. A 1.29 GB archive to R2 moved at 27 MB/s before the change while the same run read and hashed it locally at over 400 MB/s.
- **s3**: New **Parallel Upload Parts** setting on every S3 backup destination sets how many parts upload at once and how large each one may be, up to 32 parts of 64 MB. The form shows how much memory the chosen combination uses per upload.
- **s3**: Part size now adapts to the backup being uploaded, never above the configured maximum. A part size too large for a given archive used to leave connections with nothing to upload, which cost a 1.39 GB backup to Cloudflare R2 a third of its throughput at 32 parts of 64 MB.
- **s3**: The run log now records upload throughput and the part size actually used. Throughput was only ever shown in the live progress detail, which is gone once the run ends.
- **s3**: Backing up a directory from an S3 destination now reports progress while the listing runs and stops within one request when the job is cancelled. Both previously waited for the entire listing to finish.

### 📝 Documentation

- **installation**: The installation guide now recommends mounting `/tmp` so a running backup is staged outside the Docker disk, and the compose and run examples carry the volume. The file backup guide and the environment reference explain the same thing where disk space comes up. ([#145](https://github.com/Skyfay/DBackup/issues/145))
- **developer-guide**: The setup guide now points at the platform setup scripts instead of listing a shorter set of packages beside them. It also warns against installing `libpq` for PostgreSQL, whose `pg_dump` is built without LZ4 and ZSTD and breaks native compression.
- **mssql**: The guide now covers SQL Server on Windows, from the form the backup path has to take to setting up SSH mode against the Windows OpenSSH server. An SMB share is documented as the fallback where that server is unavailable. ([#148](https://github.com/Skyfay/DBackup/issues/148))

### 🧪 Tests

- **tests**: The adapter transport lint guard no longer fails under load. It imports the entire adapter registry and was running against the default 5 second limit, which every new adapter moved a little closer to the edge.

### 🔧 CI/CD

- **docker**: The image now ships SqlPackage and the .NET runtime, which the Azure SQL Database source needs. This adds roughly 270 MB on both **linux/amd64** and **linux/arm64**.
- **scripts**: The macOS and Debian development setup scripts now install SqlPackage, which the Azure SQL Database source needs. On macOS it lands in the Homebrew prefix and is wrapped so it needs neither a `PATH` entry nor a `DOTNET_ROOT` variable.

### 🐳 Docker

- **Image**: `skyfay/dbackup:vNEXT`
- **Also tagged as**: `latest`, `vNEXT`
- **CI Image**: `skyfay/dbackup:ci`
- **Platforms**: linux/amd64, linux/arm64


## v3.2.0 - Docker Volumes Backup, SSH Key Generation, MongoDB Atlas Support, and Bug Fixes
*Released: Aug 8, 2026*

Expand Down
4 changes: 4 additions & 0 deletions docs/developer-guide/adapters/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Database adapters handle the dump and restore operations for different database
| MongoDB | `mongodb` | `mongodump`, `mongorestore` | ✅ | `.archive` |
| SQLite | `sqlite` | None (file copy) | ✅ | `.db` |
| MSSQL | `mssql` | None (TDS protocol) | ✅ (TDS tunnelled) | `.bak` |
| Azure SQL Database | `azure-sql` | `sqlpackage` | ❌ (public PaaS endpoint) | `.bacpac` |
| Redis | `redis` | `redis-cli` | ✅ | `.rdb` |
| Firebird | `firebird` | `gbak`, `isql` | ✅ | `.fbk` |

Expand All @@ -28,6 +29,7 @@ getBackupFileExtension("redis"); // "rdb"
getBackupFileExtension("mongodb"); // "archive"
getBackupFileExtension("sqlite"); // "db"
getBackupFileExtension("mssql"); // "bak"
getBackupFileExtension("azure-sql"); // "bacpac"
getBackupFileExtension("firebird"); // "fbk"
```

Expand All @@ -38,6 +40,7 @@ getBackupFileExtension("firebird"); // "fbk"
| MySQL/MariaDB | `.sql` | Standard SQL dump format |
| PostgreSQL | `.sql` | SQL dump (or `.dump` for custom format) |
| MSSQL | `.bak` | Native SQL Server backup format |
| Azure SQL Database | `.bacpac` | SqlPackage data-tier application export, a ZIP so it is never recompressed |
| MongoDB | `.archive` | mongodump `--archive` format |
| Redis | `.rdb` | Redis Database snapshot format |
| SQLite | `.db` | Direct database file copy |
Expand Down Expand Up @@ -129,6 +132,7 @@ Each database adapter can optionally return size and table count information. Th
| **PostgreSQL** | `pg_database_size(datname)` | `COUNT(*)` from `information_schema.tables` (excl. system schemas) |
| **MongoDB** | Native `sizeOnDisk` from `listDatabases` command | `listCollections().length` per database |
| **MSSQL** | `sys.master_files` (`SUM(size) * 8 * 1024`) | `COUNT(*)` from `INFORMATION_SCHEMA.TABLES` |
| **Azure SQL Database** | `sys.database_files` (`SUM(size) * 8 * 1024`, data files only), one connection per database | `COUNT(*)` from `sys.tables`, same connection |
| **SQLite** | Not supported | Not supported |
| **Redis** | Not supported | Not supported |

Expand Down
Loading
Loading