auberge 0.9.0

CLI tool for managing self-hosted infrastructure with Ansible
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# Batch DNS Operations

Create multiple DNS A records in one command using `dns set-all`.

## Overview

`dns set-all` creates or updates A records for all configured application subdomains, pointing them to a specified IP address.

**Use cases:**

- Initial DNS setup for new deployment
- Bulk record creation
- Synchronizing DNS with configuration

> **Note:** For apps with `dns_record` role integration (baikal, grimmory, cockpit, colporteur, freshrss, navidrome, paperless, webdav, yourls), DNS records are provisioned automatically during deployment. `dns set-all` remains useful for apps without this integration (e.g. Blocky, Calibre), bulk IP migrations, and initial setup across all subdomains.

## Basic Usage

### With Host

```bash
auberge dns set-all --host auberge
```

Automatically uses the IP address from inventory for the specified host.

### With Explicit IP

```bash
auberge dns set-all --ip 203.0.113.10
```

Uses the provided IP address.

## How It Works

1. **Discover configured subdomains** from environment variables
   - Reads `*_SUBDOMAIN` environment vars
   - Example: `BAIKAL_SUBDOMAIN=cal`

2. **Determine target IP**
   - From `--host` (lookups IP in inventory)
   - Or from `--ip` flag

3. **Display preview** of records to create

4. **Confirm** (unless `--yes` flag used)

5. **Create/update A records** in Cloudflare

## Tailnet-only Subdomains

Some services should be reachable only by Tailscale network members while still having a real subdomain with valid HTTPS.

### How it works

1. A Cloudflare DNS A record points `<app>.<domain>` to the server's Tailscale IP (`100.x.y.z`).
2. Caddy provisions a Let's Encrypt certificate via DNS-01 challenge (Cloudflare API), so the subdomain gets valid HTTPS.
3. Caddy binds the vhost to the Tailscale interface only (`bind <tailscale-ip>`), so it is not reachable via the server's public IP even with correct SNI.
4. The Tailscale IP is in the CGNAT range (`100.64.0.0/10`), which is not routable from the public internet. Only Tailscale network members can reach it.

### Configuration

In `~/.config/auberge/config.toml`, set both a subdomain and a Tailscale IP for the app:

```toml
paperless_subdomain = "paperless"
paperless_tailscale_ip = "100.x.y.z"
```

When `<app>_tailscale_ip` is present, `dns set-all` automatically uses that IP for the app's A record instead of the server's public IP.

### Prerequisites

- Tailscale running on the server (`tailscaled.service`)
- Cloudflare API token configured (for DNS-01 challenge and DNS record management)
- Caddy built with `caddy-dns/cloudflare` module (required for DNS-01 challenge)

### dns migrate behavior

`dns migrate` skips any A record whose current IP is in the CGNAT range (`100.64.0.0/10`). This prevents tailnet-only subdomains from being accidentally migrated to a new public IP during a VPS migration.

## Example

```bash
$ auberge dns set-all --host auberge

CLOUDFLARE DNS

Creating the following A records:
  • dns.example.com → 203.0.113.10
  • lire.example.com → 203.0.113.10
  • rss.example.com → 203.0.113.10
  • musique.example.com → 203.0.113.10
  • calendrier.example.com → 203.0.113.10
  • webdav.example.com → 203.0.113.10
  • url.example.com → 203.0.113.10

Proceed? [y/N]: y

✓ Created dns.example.com
✓ Created lire.example.com
✓ Created rss.example.com
✓ Created musique.example.com
✓ Created calendrier.example.com
✓ Created webdav.example.com
✓ Created url.example.com

✓ Successfully created 7/7 A records pointing to 203.0.113.10
```

## Options

### Dry Run

Preview without making changes:

```bash
auberge dns set-all --host auberge --dry-run
```

### Skip Confirmation

Non-interactive execution:

```bash
auberge dns set-all --host auberge --yes
```

### Specific Subdomains

Only create selected records:

```bash
auberge dns set-all --host auberge --subdomains cal,rss,music
```

### Skip Subdomains

Exclude specific records:

```bash
auberge dns set-all --host auberge --skip dns,url
```

### Continue on Error

Don't stop if one record fails:

```bash
auberge dns set-all --host auberge --continue-on-error
```

## Use Cases

### Initial DNS Setup

After deploying to fresh VPS, DNS records for integrated apps are created automatically. Use `set-all` for apps without integration (e.g. Blocky, Calibre):

```bash
auberge dns set-all --host auberge --subdomains dns,lire
```

### New Application

If the new app role includes `dns_record`, its A record is created on deploy. Otherwise, create it manually:

```bash
auberge dns set-all --host auberge --subdomains newapp
```

### Selective Update

Update specific apps after migration:

```bash
# Only update media apps
auberge dns set-all --ip 10.0.0.1 --subdomains music,books
```

### CI/CD Integration

Automated DNS updates:

```bash
auberge dns set-all --host production --yes --continue-on-error
```

## Environment Variable Discovery

`set-all` reads subdomain names from environment variables:

**Format:** `{APP}_SUBDOMAIN`

**Default values (from mise.toml):**

```toml
BLOCKY_SUBDOMAIN = "dns"
CALIBRE_SUBDOMAIN = "lire"
FRESHRSS_SUBDOMAIN = "rss"
NAVIDROME_SUBDOMAIN = "musique"
BAIKAL_SUBDOMAIN = "calendrier"
WEBDAV_SUBDOMAIN = "webdav"
YOURLS_SUBDOMAIN = "url"
```

**Result:** Creates A records for:

- dns.example.com
- lire.example.com
- rss.example.com
- musique.example.com
- calendrier.example.com
- webdav.example.com
- url.example.com

### Custom Subdomains

Override defaults:

```toml
# mise.toml
BAIKAL_SUBDOMAIN = "cal"      # calendrier → cal
NAVIDROME_SUBDOMAIN = "music"   # musique → music
```

`set-all` will use `cal` and `music` instead.

## Host vs IP

### Using --host

```bash
auberge dns set-all --host auberge
```

**Behavior:**

- Looks up IP for `auberge` in inventory
- Uses `ansible_host` value
- Requires host configured in inventory

**Example inventory:**

```yaml
hosts:
  auberge:
    ansible_host: "{{ lookup('env', 'AUBERGE_HOST') }}"
```

### Using --ip

```bash
auberge dns set-all --ip 203.0.113.10
```

**Behavior:**

- Uses provided IP directly
- Doesn't require inventory
- Useful for testing or one-off operations

### Conflicts

Can't use both:

```bash
# Error: conflicts
auberge dns set-all --host auberge --ip 10.0.0.1
```

Choose one or the other.

## Filtering Options

### --subdomains (Include Only)

Create only specified subdomains:

```bash
auberge dns set-all --host auberge --subdomains cal,rss
```

**Only creates:**

- cal.example.com
- rss.example.com

**Skips:** All others (dns, music, books, etc.)

### --skip (Exclude)

Create all except specified:

```bash
auberge dns set-all --host auberge --skip dns,url
```

**Creates:** lire, rss, musique, calendrier, webdav

**Skips:** dns, url

### Combine Both

Include specific set, then exclude from that:

```bash
auberge dns set-all --host auberge --subdomains cal,rss,music --skip music
```

**Result:** Only creates cal and rss (music excluded)

## Error Handling

### Default: Fail Fast

Stops on first error:

```bash
$ auberge dns set-all --host auberge

✓ Created dns.example.com
✓ Created cal.example.com
✗ Failed rss.example.com: Rate limit exceeded
# Stops here, doesn't create remaining records
```

### With --continue-on-error

Attempts all records despite failures:

```bash
$ auberge dns set-all --host auberge --continue-on-error

✓ Created dns.example.com
✓ Created cal.example.com
✗ Failed rss.example.com: Rate limit exceeded
✓ Created music.example.com
✓ Created books.example.com
# Continues through all records

✓ Successfully created 4/5 A records pointing to 203.0.113.10
Failed to create 1 records
```

**Use case:** CI/CD where partial success is acceptable.

## Output Formats

### Human (Default)

Readable output with colors and symbols:

```bash
auberge dns set-all --host auberge
```

### JSON

Machine-readable output:

```bash
auberge dns set-all --host auberge --output json
```

**Future feature** - not yet implemented.

### TSV

Tab-separated values:

```bash
auberge dns set-all --host auberge --output tsv
```

**Future feature** - not yet implemented.

## Best Practices

### Always Dry Run First

```bash
# Preview
auberge dns set-all --host auberge --dry-run

# If looks good, execute
auberge dns set-all --host auberge
```

### Use in Deployment Scripts

```bash
#!/bin/bash
set -euo pipefail

# Deploy VPS
auberge ansible run --host production --skip-tags bootstrap

# Set up DNS
auberge dns set-all --host production --yes

# Verify
auberge dns status
```

### Selective Updates

For large configs, update only what changed:

```bash
# Only new apps
auberge dns set-all --host auberge --subdomains newapp1,newapp2
```

### Error Handling in Scripts

```bash
if ! auberge dns set-all --host auberge --yes; then
    echo "DNS setup failed, rolling back deployment"
    # Rollback logic here
    exit 1
fi
```

## Comparison with Other Commands

| Command   | Purpose                            | Creates New | Updates Existing |
| --------- | ---------------------------------- | ----------- | ---------------- |
| `set-all` | Batch create configured subdomains | Yes         | Yes              |
| `migrate` | Update all existing records        | No          | Yes              |
| `set`     | Single subdomain                   | Yes         | Yes              |

**When to use `set-all`:**

- Initial DNS setup
- Adding multiple apps
- Synchronizing DNS with config

**When to use `migrate`:**

- VPS provider migration
- IP address change for all services

**When to use `set`:**

- Single record update
- Custom subdomain not in config

## Troubleshooting

### "No subdomain environment variables found"

No `*_SUBDOMAIN` env vars configured.

**Fix:**

```bash
# Check config for *_subdomain keys
auberge config list

# If empty, verify config.toml has subdomain values
```

### "Host not found in inventory"

Specified host doesn't exist.

**Fix:**

```bash
# List available hosts
ansible-inventory -i ansible/inventory.yml --list

# Or use IP instead
auberge dns set-all --ip 203.0.113.10
```

### "Rate limit exceeded"

Too many API requests.

**Fix:** Wait 60 seconds, then retry with --continue-on-error:

```bash
auberge dns set-all --host auberge --continue-on-error
```

### Records already exist

Command is idempotent - safe to re-run:

```bash
# First run: creates records
auberge dns set-all --host auberge

# Second run: updates if IP changed, otherwise no-op
auberge dns set-all --host auberge
```

## Alias

Short form:

```bash
auberge dns sa --host auberge
auberge dns sa --ip 10.0.0.1 -n    # dry-run
auberge dns sa --host prod -y      # yes
```

## Related Pages

- [Managing Records]dns/managing-records.md - Individual record operations
- [Migration]dns/migration.md - Bulk IP migration
- [Cloudflare Setup]dns/cloudflare-setup.md - Initial configuration
- [Environment Variables]configuration/environment-variables.md - Subdomain configuration