Skip to content

Commit ccd98bd

Browse files
committed
fix: improve docker container support for config and database
- Auto-detect container environment and search /config for config files - Create parent directories before initializing SQLite database - Make initDatabase async to support directory creation - Update README with Docker volume mount documentation
1 parent f4679fb commit ccd98bd

7 files changed

Lines changed: 99 additions & 16 deletions

File tree

README.md

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,19 +409,58 @@ docker run --rm -v postgres_data_restored:/data -v /tmp/restore:/backup alpine \
409409

410410
### Docker
411411

412+
The Docker image uses three volume mount points:
413+
414+
| Mount Point | Purpose |
415+
|-------------|---------|
416+
| `/config` | Config file (`backitup.config.yaml`) and database |
417+
| `/data` | Source files to backup (or mount your own paths) |
418+
| `/backups` | Local backup storage destination |
419+
412420
```bash
413421
docker run -d --name backitup \
414-
-v ./config:/config:ro -v ./data:/data -v ./backups:/backups \
415-
-e S3_ACCESS_KEY_ID=key -e S3_SECRET_ACCESS_KEY=secret \
422+
-v ./config:/config \
423+
-v ./data:/data:ro \
424+
-v ./backups:/backups \
425+
-e S3_ACCESS_KEY_ID=key \
426+
-e S3_SECRET_ACCESS_KEY=secret \
416427
ghcr.io/climactic/backitup:latest start
417428
```
418429

430+
Your config file should reference these paths:
431+
432+
```yaml
433+
# /config/backitup.config.yaml
434+
version: "1.0"
435+
database:
436+
path: /config/backitup.db
437+
sources:
438+
app:
439+
path: /data
440+
local:
441+
enabled: true
442+
path: /backups
443+
```
444+
419445
To backup Docker volumes from within a container, mount the Docker socket:
420446

421447
```bash
422448
docker run -d --name backitup \
423449
-v /var/run/docker.sock:/var/run/docker.sock \
424-
-v ./config:/config:ro -v ./data:/data -v ./backups:/backups \
450+
-v ./config:/config \
451+
-v ./data:/data:ro \
452+
-v ./backups:/backups \
453+
ghcr.io/climactic/backitup:latest start
454+
```
455+
456+
You can also mount specific host paths instead of using `/data`:
457+
458+
```bash
459+
docker run -d --name backitup \
460+
-v ./config:/config \
461+
-v /var/www/myapp:/myapp:ro \
462+
-v /var/lib/postgres:/postgres:ro \
463+
-v ./backups:/backups \
425464
ghcr.io/climactic/backitup:latest start
426465
```
427466

@@ -433,9 +472,9 @@ services:
433472
image: ghcr.io/climactic/backitup:latest
434473
command: start
435474
volumes:
436-
- ./config:/config:ro
437-
- ./data:/data
438-
- ./backups:/backups
475+
- ./config:/config # Config + database
476+
- ./data:/data:ro # Source files (read-only)
477+
- ./backups:/backups # Local backup destination
439478
- /var/run/docker.sock:/var/run/docker.sock # For volume backups
440479
environment:
441480
- S3_ACCESS_KEY_ID=key

src/cli/commands/list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export async function listCommand(args: string[]): Promise<number> {
3838

3939
try {
4040
const config = await findAndLoadConfig(values.config);
41-
initDatabase(config.database.path);
41+
await initDatabase(config.database.path);
4242

4343
// Get backups
4444
let backups: BackupRecord[];

src/cli/commands/verify.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export async function verifyCommand(args: string[]): Promise<number> {
4141

4242
try {
4343
const config = await findAndLoadConfig(values.config);
44-
initDatabase(config.database.path);
44+
await initDatabase(config.database.path);
4545

4646
ui.intro("backitup verify");
4747

src/config/loader.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,54 @@ function parseConfigContent(content: string, ext: string): unknown {
7373
}
7474

7575
/**
76-
* Find a config file in the given directory
76+
* Check if running inside a Docker container
77+
*/
78+
function isRunningInDocker(): boolean {
79+
try {
80+
// Check for .dockerenv file (most reliable)
81+
if (Bun.file("/.dockerenv").size >= 0) {
82+
return true;
83+
}
84+
} catch {
85+
// File doesn't exist
86+
}
87+
88+
try {
89+
// Check cgroup for docker/container references
90+
const cgroup = Bun.file("/proc/1/cgroup");
91+
if (cgroup.size > 0) {
92+
// We can't easily read sync here, so just check if the file exists
93+
// The /.dockerenv check above is the primary method
94+
}
95+
} catch {
96+
// Not on Linux or file not accessible
97+
}
98+
99+
return false;
100+
}
101+
102+
/**
103+
* Find a config file in the given directory or standard locations
77104
*/
78105
export function findConfigFile(startDir: string = process.cwd()): string | null {
79106
const configNames = ["backitup.config.yaml", "backitup.config.yml", "backitup.config.json"];
107+
const searchDirs = [startDir];
108+
109+
// Only check /config when running in Docker
110+
if (isRunningInDocker()) {
111+
searchDirs.push("/config");
112+
}
80113

81-
for (const name of configNames) {
82-
const configPath = path.join(startDir, name);
83-
if (Bun.file(configPath).size > 0) {
84-
return configPath;
114+
for (const dir of searchDirs) {
115+
for (const name of configNames) {
116+
const configPath = path.join(dir, name);
117+
try {
118+
if (Bun.file(configPath).size > 0) {
119+
return configPath;
120+
}
121+
} catch {
122+
// Directory doesn't exist or not accessible, continue
123+
}
85124
}
86125
}
87126

src/core/backup/orchestrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export async function runBackup(
6262

6363
logger.info(`Starting backup: ${backupId} (schedule: ${options.schedule})`);
6464

65-
initDatabase(config.database.path);
65+
await initDatabase(config.database.path);
6666

6767
const useS3 = config.s3.enabled && !options.localOnly;
6868
const useLocal = config.local.enabled && !options.s3Only;

src/core/cleanup/orchestrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export async function runCleanup(
3535
config: BackitupConfig,
3636
options: CleanupOptions,
3737
): Promise<CleanupResult> {
38-
initDatabase(config.database.path);
38+
await initDatabase(config.database.path);
3939

4040
if (config.s3.enabled) {
4141
initS3Client(config.s3);

src/db/connection.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22
* Database connection management
33
*/
44

5+
import { mkdir } from "node:fs/promises";
6+
import { dirname } from "node:path";
57
import { Database } from "bun:sqlite";
68
import { initializeDatabase } from "./migrations";
79

810
let db: Database | null = null;
911

10-
export function initDatabase(dbPath: string): Database {
12+
export async function initDatabase(dbPath: string): Promise<Database> {
1113
if (db) {
1214
return db;
1315
}
1416

17+
// Ensure parent directory exists
18+
await mkdir(dirname(dbPath), { recursive: true });
19+
1520
db = new Database(dbPath, { create: true });
1621
initializeDatabase(db);
1722

0 commit comments

Comments
 (0)