kamal-backup 0.1.0.pre.2 → 0.1.0.pre.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f90e6e9f02fd3ddb958b6f2c41de4219b36fa3e8ff4b84ea779c7decf74379b5
4
- data.tar.gz: ab72fc616b324bcceee78c3d0060900e747334bf2ef8f3e962c0d81e0d7674ca
3
+ metadata.gz: 8ed4e21bfee7ac0e789c3a0706626062055c92701128b5999a3828f5f3190057
4
+ data.tar.gz: 5983618fb0fb16db51cfa82f4ff28398c00568d7a5082fd494a7063c944b111d
5
5
  SHA512:
6
- metadata.gz: a94b5efc6f8c9727a176899572a758247f1db0150af45f71b6a0f830c95a9294f885d26e67d7b649308f604087e9e318f427033b8428ed663671b0b1cdbda87f
7
- data.tar.gz: 97cfef328b321fefca4d95c36caf25d3a168e49b97e5de02879d3907a88d61842aa233d6dacb4367e1ac5c6866d2636586e67db91e04f2acfa02057dae318c4b
6
+ metadata.gz: 227a34dc536cd9d5ca13cb2853fcac13c03c5defbe87e406374c1ebc7b94e7cc094693903aab1e7b6addb2b2a11913bae419767cb0e242f56d86818171bacb79
7
+ data.tar.gz: 16788fa7108df55c2afa70f900511ce6574c931be8459774649ebcb533c415873e4424d8172e0df987804f6509f4aea7fb398676cd3741ff0c59fddf6d7e9a74
data/README.md CHANGED
@@ -1,40 +1,60 @@
1
1
  # kamal-backup
2
2
 
3
- `kamal-backup` is a small Docker image for Kamal accessories. It creates encrypted, restic-backed backups for self-hosted apps by backing up database dumps and mounted application files together.
3
+ `kamal-backup` gives Rails apps a clean backup and restore workflow for Kamal.
4
4
 
5
- The Docker image is the normal production interface. The Ruby gem packages the same `kamal-backup` executable so the image can install it cleanly, and so operators can optionally run the CLI from a laptop for restore drills when they have restic, database clients, and the right environment configured.
5
+ It backs up:
6
6
 
7
- It is aimed at common Kamal backup needs:
7
+ - PostgreSQL, MySQL/MariaDB, or SQLite
8
+ - file-backed Active Storage and other mounted app files
8
9
 
9
- - Kamal Postgres backup
10
- - Kamal MySQL and MariaDB backup
11
- - Kamal Active Storage backup
12
- - Kamal restic backup
13
- - Restore drills and evidence for security reviews such as CASA
10
+ It restores in two clear modes:
14
11
 
15
- ## What It Backs Up
12
+ - `restore local`: pull a production backup onto your machine
13
+ - `restore production`: restore back into live production
16
14
 
17
- `kamal-backup` handles two data surfaces:
15
+ And it drills in two clear modes:
18
16
 
19
- 1. A logical database dump from PostgreSQL, MySQL/MariaDB, or SQLite.
20
- 2. Mounted application files such as Rails Active Storage.
17
+ - `drill local`: prove the backup works on your machine
18
+ - `drill production`: restore into scratch targets on production infrastructure, run checks, and record evidence
21
19
 
22
- Database backups are logical dumps, not raw database data directories. File backups use one `restic backup` snapshot per run containing all configured mounted paths, so `restore-files latest` restores all file paths from that run.
20
+ Under the hood it uses [restic](https://restic.net/) for encrypted backup storage and repository management.
23
21
 
24
- Database dump snapshots are tagged with `kamal-backup`, `app:<name>`, `type:database`, `adapter:<adapter>`, and `run:<timestamp>`. The dump object uses a flat restic stdin filename such as `databases-chatwithwork-postgres-20260422T120000Z.pgdump` because restic stdin backups do not support nested virtual paths consistently.
22
+ ## Why Rails teams use it
25
23
 
26
- ## Quick Start With Kamal
24
+ `kamal-backup` is aimed at the common self-hosted Rails setup where:
27
25
 
28
- Add a backup accessory to your Kamal deploy config:
26
+ - the app is deployed with Kamal
27
+ - the database is PostgreSQL, MySQL/MariaDB, or SQLite
28
+ - file data lives on a mounted volume
29
+ - you need real restore drills and evidence for CASA or another security review
29
30
 
30
- ```yaml
31
- aliases:
32
- backup: accessory exec backup "kamal-backup backup"
33
- backup-list: accessory exec backup "kamal-backup list"
34
- backup-check: accessory exec backup "kamal-backup check"
35
- backup-evidence: accessory exec backup "kamal-backup evidence"
36
- backup-logs: accessory logs backup -f
31
+ If your app already stores Active Storage blobs directly in S3, there may be no local file path for `BACKUP_PATHS` to capture. In that case, `kamal-backup` still covers the database side, but object-storage backups are a separate concern.
32
+
33
+ ## Quick Start
34
+
35
+ Add the gem in your Rails app:
36
+
37
+ ```ruby
38
+ group :development do
39
+ gem "kamal-backup"
40
+ end
41
+ ```
42
+
43
+ Install it and generate the local config stubs:
44
+
45
+ ```sh
46
+ bundle install
47
+ bundle exec kamal-backup init
48
+ ```
49
+
50
+ That creates:
37
51
 
52
+ - `config/kamal-backup.yml`
53
+ - `config/kamal-backup.local.yml`
54
+
55
+ Then add the backup accessory to `config/deploy.yml`:
56
+
57
+ ```yaml
38
58
  accessories:
39
59
  backup:
40
60
  image: ghcr.io/crmne/kamal-backup:latest
@@ -64,294 +84,206 @@ bin/kamal accessory boot backup
64
84
  bin/kamal accessory logs backup
65
85
  ```
66
86
 
67
- Run manual commands:
87
+ Run the first backup from your app checkout with the local gem and Kamal-style destination selection:
68
88
 
69
89
  ```sh
70
- bin/kamal backup
71
- bin/kamal backup-list
72
- bin/kamal backup-check
73
- bin/kamal backup-evidence
74
- bin/kamal backup-logs
90
+ bundle exec kamal-backup -d production backup
91
+ bundle exec kamal-backup -d production list
92
+ bundle exec kamal-backup -d production evidence
75
93
  ```
76
94
 
77
- Alias reference:
78
-
79
- | Alias | Expands to | Use |
80
- |---|---|---|
81
- | `bin/kamal backup` | `accessory exec backup "kamal-backup backup"` | Run one backup immediately. |
82
- | `bin/kamal backup-list` | `accessory exec backup "kamal-backup list"` | Show restic snapshots for the configured app. |
83
- | `bin/kamal backup-check` | `accessory exec backup "kamal-backup check"` | Run `restic check` and store the latest check result. |
84
- | `bin/kamal backup-evidence` | `accessory exec backup "kamal-backup evidence"` | Print redacted backup evidence JSON. |
85
- | `bin/kamal backup-logs` | `accessory logs backup -f` | Tail the backup accessory logs. |
86
-
87
- ## Commands
88
-
89
- Commands usually run inside the production backup accessory with `bin/kamal accessory exec backup "kamal-backup <command>"`, or through Kamal aliases such as `bin/kamal backup`. A local gem install is useful when you intentionally want the operator laptop to run restic and database client commands directly.
95
+ If you keep multiple deploy configs, pass `-c` the same way Kamal does:
90
96
 
91
97
  ```sh
92
- kamal-backup backup
93
- kamal-backup restore-db [snapshot-or-latest]
94
- kamal-backup restore-files [snapshot-or-latest] [target-dir]
95
- kamal-backup list
96
- kamal-backup check
97
- kamal-backup evidence
98
- kamal-backup schedule
99
- kamal-backup version
98
+ bundle exec kamal-backup -c config/deploy.staging.yml -d staging backup
100
99
  ```
101
100
 
102
- | Command | What it does |
103
- |---|---|
104
- | `backup` | Runs one immediate backup, creating one database snapshot and one file snapshot for all `BACKUP_PATHS`. |
105
- | `restore-db [snapshot-or-latest]` | Restores a database dump. Defaults to `latest` and requires explicit restore environment. |
106
- | `restore-files [snapshot-or-latest] [target-dir]` | Restores file paths from a file snapshot. Defaults to `latest /restore/files`. |
107
- | `list` | Lists restic snapshots for the configured app tags. |
108
- | `check` | Runs `restic check` and records the latest result for evidence output. |
109
- | `evidence` | Prints redacted JSON with backup configuration, latest snapshots, check status, and tool versions. |
110
- | `schedule` | Runs the foreground scheduler loop used by the container default command. |
111
- | `version` | Prints the gem version. `--version` and `-v` do the same. |
101
+ Examples live in:
112
102
 
113
- The default container command is:
103
+ - [examples/kamal-accessory.yml](examples/kamal-accessory.yml)
104
+ - [examples/kamal-backup.yml.example](examples/kamal-backup.yml.example)
105
+ - [examples/kamal-backup.local.yml.example](examples/kamal-backup.local.yml.example)
114
106
 
115
- ```sh
116
- kamal-backup schedule
117
- ```
107
+ ## What Restic Does Here
118
108
 
119
- ## Configuration
109
+ `kamal-backup` uses restic as the backup engine and repository format.
120
110
 
121
- Required common environment:
111
+ In the normal Kamal setup, you do not install restic on the Rails app host. The backup accessory image already includes it. You only point the accessory at a restic repository, usually:
122
112
 
123
- ```sh
124
- APP_NAME=chatwithwork
125
- DATABASE_ADAPTER=postgres
126
- RESTIC_REPOSITORY=s3:https://s3.example.com/chatwithwork-backups
127
- RESTIC_PASSWORD=change-me
128
- BACKUP_PATHS=/data/storage
129
- ```
113
+ - S3-compatible object storage
114
+ - a restic REST server
115
+ - a filesystem path for local development
130
116
 
131
- `BACKUP_PATHS` accepts colon-separated or newline-separated paths. Every path must exist. Suspicious broad paths such as `/`, `/var`, `/etc`, and `/root` are refused unless `KAMAL_BACKUP_ALLOW_SUSPICIOUS_PATHS=true`.
117
+ If you choose a `rest:` repository, `kamal-backup` does not install or operate that server for you. It is a separate service.
132
118
 
133
- PostgreSQL:
119
+ ## Commands
134
120
 
135
- ```sh
136
- DATABASE_ADAPTER=postgres
137
- DATABASE_URL=postgres://app@app-db:5432/app_production
138
- PGPASSWORD=change-me
139
- ```
140
-
141
- MySQL/MariaDB:
121
+ The operator-facing command surface is:
142
122
 
143
123
  ```sh
144
- DATABASE_ADAPTER=mysql
145
- DATABASE_URL=mysql2://app@app-mysql:3306/app_production
146
- MYSQL_PWD=change-me
124
+ kamal-backup init
125
+ kamal-backup backup
126
+ kamal-backup restore local [snapshot-or-latest]
127
+ kamal-backup restore production [snapshot-or-latest]
128
+ kamal-backup drill local [snapshot-or-latest]
129
+ kamal-backup drill production [snapshot-or-latest]
130
+ kamal-backup list
131
+ kamal-backup check
132
+ kamal-backup evidence
133
+ kamal-backup schedule
134
+ kamal-backup version
147
135
  ```
148
136
 
149
- SQLite:
137
+ Production-side commands shell out through Kamal when you pass `-d` or `-c`. Local commands run on your machine.
150
138
 
151
- ```sh
152
- DATABASE_ADAPTER=sqlite
153
- SQLITE_DATABASE_PATH=/data/db/production.sqlite3
154
- ```
155
-
156
- Retention defaults:
139
+ Common examples:
157
140
 
158
141
  ```sh
159
- RESTIC_KEEP_LAST=7
160
- RESTIC_KEEP_DAILY=7
161
- RESTIC_KEEP_WEEKLY=4
162
- RESTIC_KEEP_MONTHLY=6
163
- RESTIC_KEEP_YEARLY=2
164
- RESTIC_FORGET_AFTER_BACKUP=true
142
+ bundle exec kamal-backup -d production backup
143
+ bundle exec kamal-backup -d production check
144
+ bundle exec kamal-backup -d production evidence
145
+ bundle exec kamal-backup -d production restore production latest
146
+ bundle exec kamal-backup -d production drill production latest --database app_restore_20260423 --files /restore/files
147
+ bundle exec kamal-backup -d production version
148
+ bundle exec kamal-backup restore local latest
149
+ bundle exec kamal-backup drill local latest --check "bin/rails runner 'puts User.count'"
165
150
  ```
166
151
 
167
- Set `RESTIC_FORGET_AFTER_BACKUP=false` for append-only repositories, such as a restic REST server started with `--append-only`. In that mode, run retention and prune from the backup server or another trusted maintenance process with delete permissions.
152
+ Use `kamal-backup help`, `kamal-backup help restore`, or `kamal-backup help drill` for task-specific usage.
168
153
 
169
- Scheduler and checks:
154
+ ## How a Backup Run Works
170
155
 
171
- ```sh
172
- BACKUP_SCHEDULE_SECONDS=86400
173
- BACKUP_START_DELAY_SECONDS=0
174
- RESTIC_CHECK_AFTER_BACKUP=false
175
- RESTIC_CHECK_READ_DATA_SUBSET=5%
176
- ```
156
+ When `kamal-backup backup` runs, it does five things:
177
157
 
178
- For S3-compatible restic repositories, provide the standard restic/AWS variables as Kamal secrets:
158
+ 1. Validates the app name, restic repository, database settings, and `BACKUP_PATHS`.
159
+ 2. Creates a database backup with the database-native export tool.
160
+ 3. Streams that database backup into restic with tags such as `type:database`, `adapter:<adapter>`, and `run:<timestamp>`.
161
+ 4. Runs one `restic backup` for all configured `BACKUP_PATHS`, tagged as `type:files` with the same `run:<timestamp>`.
162
+ 5. Optionally runs `restic forget --prune` and `restic check`.
179
163
 
180
- ```sh
181
- AWS_ACCESS_KEY_ID=...
182
- AWS_SECRET_ACCESS_KEY=...
183
- AWS_DEFAULT_REGION=...
184
- ```
164
+ That shared `run:<timestamp>` tag lets you match the database backup and file backup from the same run.
185
165
 
186
- ## Restore Drills
166
+ ## Restore
187
167
 
188
- Restores are intentionally hard to run by accident. Every restore command requires:
168
+ `restore` means "put data back."
189
169
 
190
- ```sh
191
- KAMAL_BACKUP_ALLOW_RESTORE=true
192
- ```
170
+ `restore local` runs on your machine. With `-d` or `-c`, it asks Kamal for the backup accessory config and uses that as the source of truth for:
193
171
 
194
- Database restores use restore-specific environment by default. They do not restore to `DATABASE_URL`.
172
+ - `APP_NAME`
173
+ - `DATABASE_ADAPTER`
174
+ - `RESTIC_REPOSITORY`
175
+ - `LOCAL_RESTORE_SOURCE_PATHS` from the accessory `BACKUP_PATHS`
195
176
 
196
- PostgreSQL restore:
177
+ You still provide the local targets yourself in `config/kamal-backup.local.yml` or env:
197
178
 
198
- ```sh
199
- bin/kamal accessory exec backup \
200
- --env KAMAL_BACKUP_ALLOW_RESTORE=true \
201
- --env RESTORE_DATABASE_URL=postgres://app@app-db:5432/app_restore \
202
- "kamal-backup restore-db latest"
203
- ```
179
+ - `DATABASE_URL` or `SQLITE_DATABASE_PATH`
180
+ - `BACKUP_PATHS`
181
+ - local secrets such as `RESTIC_PASSWORD` and DB passwords
204
182
 
205
- MySQL/MariaDB restore:
183
+ Example:
206
184
 
207
185
  ```sh
208
- bin/kamal accessory exec backup \
209
- --env KAMAL_BACKUP_ALLOW_RESTORE=true \
210
- --env RESTORE_DATABASE_URL=mysql2://app@app-mysql:3306/app_restore \
211
- "kamal-backup restore-db latest"
186
+ bundle exec kamal-backup -d production restore local latest
212
187
  ```
213
188
 
214
- SQLite restore:
189
+ `restore production` is the emergency path back into the live production database and live production file paths:
215
190
 
216
191
  ```sh
217
- bin/kamal accessory exec backup \
218
- --env KAMAL_BACKUP_ALLOW_RESTORE=true \
219
- --env RESTORE_SQLITE_DATABASE_PATH=/restore/db/restore.sqlite3 \
220
- "kamal-backup restore-db latest"
192
+ bundle exec kamal-backup -d production restore production latest
221
193
  ```
222
194
 
223
- File restore:
195
+ It prompts locally, then shells out through Kamal to the backup accessory.
224
196
 
225
- ```sh
226
- bin/kamal accessory exec backup \
227
- --env KAMAL_BACKUP_ALLOW_RESTORE=true \
228
- "kamal-backup restore-files latest /restore/files"
229
- ```
197
+ ## Restore Drills
198
+
199
+ `drill` means "restore, check, and record the result."
230
200
 
231
- Restore targets that look production-like are refused unless:
201
+ `drill local` is often the fastest proof for a small app:
232
202
 
233
203
  ```sh
234
- KAMAL_BACKUP_ALLOW_PRODUCTION_RESTORE=true
204
+ bundle exec kamal-backup -d production drill local latest --check "bin/rails runner 'puts User.count'"
235
205
  ```
236
206
 
237
- File restores to configured backup paths are refused unless:
207
+ `drill production` restores into scratch targets on production infrastructure. It does not touch the live production database:
238
208
 
239
209
  ```sh
240
- KAMAL_BACKUP_ALLOW_IN_PLACE_FILE_RESTORE=true
210
+ bundle exec kamal-backup -d production drill production latest \
211
+ --database app_restore_20260423 \
212
+ --files /restore/files \
213
+ --check "test -d /restore/files/data/storage"
241
214
  ```
242
215
 
243
- ## Evidence
216
+ Every drill writes `last_restore_drill.json` under `KAMAL_BACKUP_STATE_DIR`, and `kamal-backup evidence` includes that latest result.
244
217
 
245
- `kamal-backup evidence` prints a redacted JSON summary suitable for operational evidence:
246
-
247
- - app name
248
- - current time
249
- - database adapter
250
- - redacted restic repository
251
- - configured file backup paths
252
- - whether client-side forget/prune is enabled
253
- - retention policy
254
- - latest database and file snapshots
255
- - last tracked `restic check` result
256
- - image version
257
- - installed tool versions
218
+ ## Evidence for CASA and Similar Reviews
258
219
 
259
- Secrets, passwords, access keys, and database URL credentials are redacted.
220
+ `evidence` is the JSON summary you can attach to an ops record or security review.
260
221
 
261
- Run:
262
-
263
- ```sh
264
- bin/kamal accessory exec backup "kamal-backup evidence"
265
- ```
222
+ It includes:
266
223
 
267
- ## Local Development
268
-
269
- Run tests:
224
+ - latest database and file snapshots
225
+ - latest `restic check` result
226
+ - latest restore drill result
227
+ - retention settings
228
+ - tool versions
270
229
 
271
- ```sh
272
- bin/test
273
- ```
230
+ For many reviews, the useful sequence is:
274
231
 
275
- Run docs locally:
232
+ 1. scheduled backups
233
+ 2. repository checks
234
+ 3. a real restore drill
235
+ 4. `kamal-backup evidence`
276
236
 
277
- ```sh
278
- cd docs
279
- bundle install
280
- bundle exec jekyll serve --livereload
281
- ```
237
+ That reads much better to a reviewer than "the backup job is green."
282
238
 
283
- Published docs are configured for `https://kamal-backup.dev`.
239
+ ## Configuration Highlights
284
240
 
285
- Build the image:
241
+ Core accessory environment:
286
242
 
287
243
  ```sh
288
- docker build -t kamal-backup .
244
+ APP_NAME=chatwithwork
245
+ DATABASE_ADAPTER=postgres
246
+ RESTIC_REPOSITORY=s3:https://s3.example.com/chatwithwork-backups
247
+ RESTIC_PASSWORD=change-me
248
+ BACKUP_PATHS=/data/storage
289
249
  ```
290
250
 
291
- CI publishes container images to `ghcr.io/crmne/kamal-backup`. Pull requests build the image without pushing; branch, tag, SHA, default-branch `latest`, and default-branch version tags are pushed on non-PR builds. The version tag comes from `lib/kamal_backup/version.rb`, matching the gem version.
292
-
293
- The CLI is packaged as the `kamal-backup` gem. The Docker image builds and installs that gem, which is why `kamal-backup` is on `PATH` inside the container. On default-branch CI, a new gem version is published to RubyGems and GitHub Packages when it does not already exist. The RubyGems publish step expects the repository secret `RUBYGEMS_AUTH_TOKEN`.
294
-
295
- For local Ruby use:
251
+ PostgreSQL:
296
252
 
297
253
  ```sh
298
- gem build kamal-backup.gemspec
299
- gem install ./kamal-backup-*.gem
300
- kamal-backup --help
254
+ DATABASE_ADAPTER=postgres
255
+ DATABASE_URL=postgres://app@app-db:5432/app_production
256
+ PGPASSWORD=change-me
301
257
  ```
302
258
 
303
- In normal Kamal use, you do not need to install the gem on the app host. Run the command inside the accessory:
259
+ MySQL/MariaDB:
304
260
 
305
261
  ```sh
306
- bin/kamal accessory exec backup "kamal-backup evidence"
262
+ DATABASE_ADAPTER=mysql
263
+ DATABASE_URL=mysql2://app@app-mysql:3306/app_production
264
+ MYSQL_PWD=change-me
307
265
  ```
308
266
 
309
- Run a local backup against a filesystem restic repository:
267
+ SQLite:
310
268
 
311
269
  ```sh
312
- export APP_NAME=local-app
313
- export DATABASE_ADAPTER=sqlite
314
- export SQLITE_DATABASE_PATH=/tmp/app.sqlite3
315
- export BACKUP_PATHS=/tmp/app-files
316
- export RESTIC_REPOSITORY=/tmp/kamal-backup-restic
317
- export RESTIC_PASSWORD=local-password
318
- export RESTIC_INIT_IF_MISSING=true
319
-
320
- kamal-backup backup
321
- kamal-backup list
322
- kamal-backup evidence
270
+ DATABASE_ADAPTER=sqlite
271
+ SQLITE_DATABASE_PATH=/data/db/production.sqlite3
323
272
  ```
324
273
 
325
- An example Docker Compose setup for local integration work is in `examples/docker-compose.integration.yml`.
326
-
327
- ## Container Contents
328
-
329
- The image is based on Debian slim Ruby and includes:
330
-
331
- - Ruby runtime
332
- - `pg_dump` and `pg_restore`
333
- - `mariadb-dump` or `mysqldump`, plus `mariadb` or `mysql`
334
- - `sqlite3`
335
- - `restic`
336
- - CA certificates
337
- - `tini`
338
-
339
- ## Security Notes
274
+ Local config files:
340
275
 
341
- - Subprocesses are executed with argument arrays, not shell interpolation.
342
- - The CLI redacts secrets in errors and evidence output.
343
- - Database backups use logical dump tools.
344
- - File data should be mounted read-only in the backup accessory.
345
- - Restores require explicit environment flags.
346
- - Object storage credentials should be least-privilege for the backup bucket or prefix.
276
+ - `config/kamal-backup.yml`
277
+ - `config/kamal-backup.local.yml`
347
278
 
348
- ## Non-Goals
279
+ Keep secrets such as `RESTIC_PASSWORD`, cloud credentials, and local DB passwords in environment variables, not in YAML files.
349
280
 
350
- - Not a hosted backup service.
351
- - Not a replacement for database point-in-time recovery.
352
- - Not a physical replication tool.
353
- - Not a secret manager.
281
+ ## Docs
354
282
 
355
- ## License
283
+ Full docs live in [`docs/`](docs/):
356
284
 
357
- MIT
285
+ - [`docs/_guide/getting-started.md`](docs/_guide/getting-started.md)
286
+ - [`docs/_guide/configuration.md`](docs/_guide/configuration.md)
287
+ - [`docs/_guide/restore.md`](docs/_guide/restore.md)
288
+ - [`docs/_guide/restore-drills.md`](docs/_guide/restore-drills.md)
289
+ - [`docs/_reference/commands.md`](docs/_reference/commands.md)
data/exe/kamal-backup CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
4
+
5
+ require "bundler/setup"
6
+
3
7
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
4
8
 
5
9
  require "kamal_backup"