A free, open-source, cross-platform .NET 8 console app that automatically backs up the full schema and data of one or more databases on a schedule, keeping only the newest N backups. Point it at Oracle, SQL Server, MySQL, MariaDB, PostgreSQL, MongoDB, or SQLite β the engine is picked entirely by config, never by code.
Built for the "I just want a reliable weekly database backup with automatic
rotation, and I don't want to hand-roll a PowerShell/bash script per database
engine" problem. No paid product, no agent to install, no cloud dependency β it
shells out to each database's own official, battle-tested backup tool
(exp, sqlcmd, mysqldump, pg_dump, mongodump, sqlite3) and manages the
scheduling glue and retention around them.
- Multiple database engines in one tool β Oracle, SQL Server, MySQL, MariaDB, PostgreSQL, MongoDB, SQLite. Add a job per database in one config file.
- Multiple databases backed up simultaneously β every job in
appsettings.jsonruns concurrently in the same execution. One failing job never blocks or is blocked by the others. - Automatic retention β keeps only the newest N backup runs per database (default 4), deleting older ones after every successful run. A failed run is never counted or kept.
- Config-only setup, zero code changes β moving this to a new server or
pointing it at a different database is editing
appsettings.json, not recompiling anything. - Zero NuGet dependencies β every provider shells to that engine's own native CLI tool rather than reimplementing schema/data export in C#, so there's nothing fragile to keep in sync with database version changes.
- Runs once and exits β designed to be triggered by Windows Task Scheduler or cron, not to run as a background service. Simpler, and a crashed scheduler can't silently kill an internal timer loop the way it could with a long-running daemon.
- Passwords kept off the process command line where the underlying tool supports
it (PostgreSQL's
PGPASSWORDenv var, temp credential files for MySQL/Oracle, deleted immediately after use) β see π Security notes.
| Database | Native tool used | Notes |
|---|---|---|
| π΄ Oracle | exp (classic Export) |
Deliberately not Data Pump (expdp) β see why |
| π¦ SQL Server | sqlcmd (BACKUP DATABASE) |
Has an important remote-path caveat β read this |
| π¬ MySQL | mysqldump |
|
| π« MariaDB | mysqldump |
Wire-compatible with MySQL's dump format β use DbType: "MariaDb" or "MySql", same config shape |
| π PostgreSQL | pg_dump (custom format) |
|
| π MongoDB | mongodump |
|
| π SQLite | sqlite3 (.backup command) |
Uses SQLite's official online backup API, safe against a live database |
- Install the .NET 8 SDK or Runtime and whichever native tool(s) your database(s) need (see table above) on the machine that will run this app.
- Clone this repo and copy the example config:
git clone https://github.com/zawad-monsur/DbBackupTool.git cd DbBackupTool cp appsettings.example.json appsettings.json - Edit
appsettings.jsonβ keep only the job(s) you need from the example, fill in your real connection details. See βοΈ Configuration reference below for every field. - Build and run:
Or publish a standalone build to deploy elsewhere:
dotnet build -c Release dotnet bin/Release/net8.0/DbBackupTool.dll
dotnet publish -c Release -r win-x64 --self-contained false -o ./publish # or -r linux-x64 for a Linux target
- Check
{BackupFolder}\backup.log(or the console) to confirm it worked, then β° schedule it to run automatically.
appsettings.json is gitignored on purpose β your real credentials never get
committed even by accident. appsettings.example.json is the only config file
this repo tracks.
Each entry in appsettings.json's Jobs array is one independent backup job:
{
"Jobs": [
{
"Label": "MyOracleDb",
"DbType": "Oracle",
"BackupFolder": "C:\\DB_Backup\\MyOracleDb",
"RetentionCount": 4,
"Oracle": { "...": "..." }
}
]
}On each run, every job:
- Creates a timestamped subfolder:
{Label}_{yyyyMMdd_HHmmss}\. - Shells out to that engine's native backup tool, writing its output into that subfolder.
- On success, deletes every subfolder for that
Labelbeyond the newestRetentionCount. On failure, deletes just its own partial subfolder and leaves everything else untouched.
All jobs run concurrently (Task.WhenAll), so a single scheduled trigger can
back up several databases β even different engines β at once. Every job logs to
its own {BackupFolder}\backup.log, tagged with its Label, so concurrent output
stays attributable. The process exits 0 only if every job succeeded, 1 if any
failed β Task Scheduler/cron surface this directly in their own run history.
Every job needs Label, DbType, BackupFolder, and the settings block matching
DbType. RetentionCount defaults to 4 if omitted.
π΄ Oracle
"Oracle": {
"TnsConnectString": "host:1521/service_name",
"Username": "myuser",
"Password": "mypassword",
"Schema": "MYSCHEMA",
"ExpPath": "exp"
}Requires exp (Oracle's classic Export client) installed and on PATH, or set
ExpPath to its full location. Ships with an Oracle Instant Client "Tools"
package or a full Oracle Client install.
π¦ SQL Server
"SqlServer": {
"Server": "host\\INSTANCE",
"Database": "MyDatabase",
"UseWindowsAuth": true,
"Username": "",
"Password": "",
"SqlcmdPath": "sqlcmd",
"ServerSideBackupPath": "C:\\DB_Backup\\MyDatabase"
}Requires sqlcmd on PATH. Read the caveat below before using this one.
π¬ MySQL / π« MariaDB
"MySql": {
"Host": "host",
"Port": 3306,
"Username": "myuser",
"Password": "mypassword",
"Database": "mydatabase",
"MysqldumpPath": "mysqldump"
}Requires mysqldump on PATH. Use DbType: "MariaDb" for a MariaDB target β
same settings shape, same tool.
π PostgreSQL
"PostgreSql": {
"Host": "host",
"Port": 5432,
"Username": "myuser",
"Password": "mypassword",
"Database": "mydatabase",
"PgDumpPath": "pg_dump"
}Requires pg_dump on PATH. Produces a custom-format (-Fc) dump, restorable
selectively via pg_restore.
π MongoDB
"MongoDb": {
"Host": "host",
"Port": 27017,
"Username": "myuser",
"Password": "mypassword",
"Database": "mydatabase",
"AuthSource": "admin",
"MongodumpPath": "mongodump"
}Requires mongodump on PATH. Leave Username/Password empty for an
unauthenticated instance.
π SQLite
"Sqlite": {
"DatabasePath": "C:\\MyApp\\data\\app.db",
"Sqlite3Path": "sqlite3"
}Requires the sqlite3 CLI on PATH. No host/port/credentials β SQLite is just a
file.
Oracle Data Pump (expdp) always writes its dump file on the database server's
own disk, via a DIRECTORY object β getting that file onto a different machine
needs a network share the DB server can write to, which is a separate, fragile
thing to set up. The classic exp utility instead streams the dump through the
client connection straight into a local file on whichever machine runs it. If this
tool runs on a different box than the database (the common case β a dedicated
backup/ops server, or the app server pulling a backup of its own remote DB) and
needs the file to land in a local folder, exp is the right tool for that,
even though it's the "older" one. If your Oracle Instant Client only ships
expdp/impdp (some newer Instant Client "Tools" packages dropped classic
exp/imp), you'll need an older Instant Client version that still includes it.
Read this before pointing the tool at a SQL Server database.
BACKUP DATABASE ... TO DISK is executed by the SQL Server engine itself β the
path is resolved against its filesystem view, on whatever machine SQL Server
runs on, not the machine running this tool. If SQL Server is remote from wherever
you run this app:
SqlServer:ServerSideBackupPathmust be a UNC path (\\thisToolsMachine\ShareName\...) that the SQL Server service account (not your own login) has write access to, or- run this tool on the SQL Server box itself, in which case a local
C:\...path is fine.
Get this wrong and it doesn't fail obviously β you'll see "Cannot open backup
device... Access is denied" from the engine's own service account's point of view,
not yours. Oracle's exp, PostgreSQL's pg_dump, and MySQL's mysqldump don't
have this problem β they stream the file back to the client (this tool), so their
backup folders are always local from this app's perspective.
appsettings.jsonholds plaintext credentials. It's gitignored here for a reason β never commit your real one.- Where the underlying tool supports it, this app avoids putting the password on
the process command line (visible to anything reading the process list while the
backup runs):
- PostgreSQL: password passed via the
PGPASSWORDenvironment variable (Postgres's own documented mechanism). - MySQL/MariaDB: a temporary
--defaults-extra-fileis written just before the run and deleted immediately after, regardless of success or failure. - Oracle: a temporary
expparameter file (PARFILE) is used the same way. - SQL Server: prefer
UseWindowsAuth: true(integrated auth viasqlcmd -E) over SQL auth wherever possible β avoids a plaintext credential in the config file entirely. - MongoDB:
mongodumphas no equally standard non-interactive credentials mechanism across its versions, so the password is passed as a plain argument here. If that matters for your setup, scope that MongoDB user's permissions narrowly and/or run this tool under an account other processes on the box can't inspect.
- PostgreSQL: password passed via the
- Whatever OS account runs the scheduled task needs write access to
BackupFolderand (for SQL Server) the DB engine needs write access toServerSideBackupPathβ scope both as narrowly as your environment allows.
This app runs once and exits β it does not schedule itself.
Windows Task Scheduler:
schtasks /Create /TN "DB Backup" /TR "C:\Tools\DbBackupTool\DbBackupTool.exe" /SC WEEKLY /D SUN /ST 02:00 /RU SYSTEM /RL HIGHEST
Or via the GUI: Task Scheduler β Create Task β Trigger: Weekly β Action: Start a
program β path to the .exe β "Run whether user is logged on or not".
cron (Linux/macOS):
0 2 * * 0 dotnet /opt/dbbackuptool/DbBackupTool.dll >> /var/log/dbbackuptool-cron.log 2>&1
(Every Sunday at 2 AM. Adjust for your publish output β a self-contained publish
doesn't need the dotnet prefix.)
Either way, the exit code (0/1) is your immediate success/failure signal β
backup.log next to each job's backups has the detail when something fails.
Every engine is one class implementing IBackupProvider
(Task RunAsync(string backupDir, string fileBaseName, JobLog log, CancellationToken ct)),
registered in one switch in Program.cs. If you add support for something not
listed above (Redis, Cassandra, DB2, whatever), a PR is welcome.
Issues and pull requests welcome β bug reports, a new database engine, a platform quirk this hasn't hit yet. Keep new providers consistent with the existing ones: shell out to the engine's own native tool, never hand-roll an export.
MIT β use it, fork it, ship it internally, whatever you need.