# Services

This page covers how to deploy and manage containerized services: Rediaccfiles, service networking, starting/stopping, bulk operations, and autostart.

## The Rediaccfile

The **Rediaccfile** is a Bash script that defines how your services are started and stopped. It is **sourced** (not executed as a separate process), so its functions share the same shell context and have access to all exported environment variables. It must be named `Rediaccfile` or `rediaccfile` (case-insensitive) and placed inside the repository's mounted filesystem.

Rediaccfiles are discovered in two locations:
1. The **root** of the repository mount path
2. **First-level subdirectories** of the mount path (not recursive)

Hidden directories (names starting with `.`) are skipped.

### Lifecycle Functions

A Rediaccfile contains up to two functions:

| Function | When it runs | Purpose | Error behavior |
|----------|-------------|---------|----------------|
| `up()` | When starting | Start services (e.g., `renet compose -- up -d`) | Root failure is **critical** (stops everything). Subdirectory failures are **non-critical** (logged, continues) |
| `down()` | When stopping | Stop services (e.g., `renet compose -- down`) | **Best-effort** -- failures are logged but all Rediaccfiles are always attempted |

Both functions are optional. If a function is not defined, it is silently skipped.

### Execution Order

- **Starting (`up`):** Root Rediaccfile first, then subdirectories in **alphabetical order** (A to Z).
- **Stopping (`down`):** Subdirectories in **reverse alphabetical order** (Z to A), then root last.

### Environment Variables

When a Rediaccfile function executes, the following environment variables are available:

| Variable | Description | Example |
|----------|-------------|---------|
| `REDIACC_WORKING_DIR` | Mount path of the repository | `/mnt/rediacc/mounts/abc123` |
| `REDIACC_REPOSITORY` | Repository GUID | `a1b2c3d4-e5f6-...` |
| `REDIACC_NETWORK_ID` | Network ID (integer) | `2816` |
| `DOCKER_HOST` | Docker socket for this repository's isolated daemon | `unix:///var/run/rediacc/docker-2816.sock` |
| `{SERVICE}_IP` | Loopback IP for each service defined in `.rediacc.json` | `POSTGRES_IP=127.0.11.2` |

The `{SERVICE}_IP` variables are auto-generated from the slot mappings in `.rediacc.json` and exported before your Rediaccfile functions run. The naming convention converts the service name to uppercase with hyphens replaced by underscores, then appends `_IP`. For example, a service named `listmonk-app` with slot `0` becomes `LISTMONK_APP_IP=127.0.11.2`.

> **Warning: Do not use `sudo docker` in Rediaccfiles.** The `sudo` command resets environment variables, which means `DOCKER_HOST` is lost and Docker commands will target the system daemon instead of the repository's isolated daemon. This breaks container isolation and can cause port conflicts. Rediacc will block execution if it detects `sudo docker` without `-E`.
>
> Use `renet compose` in your Rediaccfiles, it automatically handles `DOCKER_HOST`, injects networking labels for route discovery, and configures service networking. See [Networking](/en/docs/networking) for details on how services are exposed via the reverse proxy. If calling Docker directly, use `docker` without `sudo`, Rediaccfile functions already run with sufficient privileges. If you must use sudo, use `sudo -E docker` to preserve environment variables.
>
> `renet` is the remote low-level tool. For normal user workflows from your workstation, prefer `rdc` commands such as `rdc repo up` and `rdc repo down`. See [rdc vs renet](/en/docs/rdc-vs-renet).

### Example

```bash
#!/bin/bash

up() {
    echo "Starting services..."
    renet compose -- up -d
}

down() {
    echo "Stopping services..."
    renet compose -- down
}
```

> **Important:** Always use `renet compose --` instead of `docker compose`. The `renet compose` wrapper enforces host networking, IP allocation, and service discovery labels required by renet-proxy. CRIU checkpoint/restore capabilities are added to containers with the `rediacc.checkpoint=true` label. Direct `docker compose` usage is rejected by Rediaccfile validation. See [Networking](/en/docs/networking) for details.

### Multi-Service Layout

For projects with multiple independent service groups, use subdirectories:

```
/mnt/rediacc/repos/my-app/
├── Rediaccfile              # Root: shared setup
├── docker-compose.yml
├── database/
│   ├── Rediaccfile          # Database services
│   └── docker-compose.yml
├── backend/
│   ├── Rediaccfile          # API server
│   └── docker-compose.yml
└── monitoring/
    ├── Rediaccfile          # Prometheus, Grafana, etc.
    └── docker-compose.yml
```

Execution order for `up`: root, then `backend`, `database`, `monitoring` (A-Z).
Execution order for `down`: `monitoring`, `database`, `backend`, then root (Z-A).

## Service Networking (.rediacc.json)

Each repository gets a /26 subnet (64 IPs) in the `127.x.x.x` loopback range. Services bind to unique loopback IPs so they can run on the same ports without conflicts.

### The .rediacc.json File

Maps service names to **slot** numbers. Each slot corresponds to a unique IP address within the repository's subnet.

```json
{
  "services": {
    "api": {"slot": 0},
    "postgres": {"slot": 1},
    "redis": {"slot": 2}
  }
}
```

### Auto-Generation from Docker Compose

You do not need to create `.rediacc.json` manually. When you run `rdc repo up`, Rediacc automatically:

1. Scans all directories containing a Rediaccfile for compose files (`docker-compose.yml`, `docker-compose.yaml`, `compose.yml`, or `compose.yaml`)
2. Extracts service names from the `services:` section
3. Assigns the next available slot to new services
4. Saves the result to `{repository}/.rediacc.json`

### IP Calculation

The IP for a service is calculated from the repository's network ID and the service's slot. The network ID is split across the second, third, and fourth octets of a `127.x.y.z` loopback address. Services start at offset 2:

| Offset | Address | Purpose |
|--------|---------|---------|
| .0 | `127.0.11.0` | Network address (reserved) |
| .1 | `127.0.11.1` | Gateway (reserved) |
| .2 – .62 | `127.0.11.2` – `127.0.11.62` | Services (`slot + 2`) |
| .63 | `127.0.11.63` | Broadcast (reserved) |

**Example** for network ID `2816` (`0x0B00`), base address `127.0.11.0`:

| Service | Slot | IP Address |
|---------|------|------------|
| api | 0 | `127.0.11.2` |
| postgres | 1 | `127.0.11.3` |
| redis | 2 | `127.0.11.4` |

Each repository supports up to **61 services** (slots 0 through 60).

### Using Service IPs in Docker Compose

Since each repository runs an isolated Docker daemon, `renet compose` automatically configures `network_mode: host` for all services. Bind services to their assigned loopback IPs:

```yaml
services:
  postgres:
    image: postgres:16
    environment:
      PGDATA: /var/lib/postgresql/data
      POSTGRES_PASSWORD: secret
    command: -c listen_addresses=${POSTGRES_IP} -c port=5432

  api:
    image: my-api:latest
    environment:
      DATABASE_URL: postgresql://postgres:secret@${POSTGRES_IP}:5432/mydb
      LISTEN_ADDR: ${API_IP}:8080
```

> **Note:** Do not add `network_mode: host` manually, `renet compose` injects it automatically. Restart policies (e.g., `restart: always`) are safe to use, renet auto-strips them for CRIU compatibility and the router watchdog handles container recovery.

> **Note:** Fork repos get flat auto-routes: `{service}-{tag}.{machine}.{baseDomain}`. Custom domains are skipped for forks.

## Starting Services

Mount the repository and start all services:

```bash
rdc repo up --name my-app -m server-1 --mount
```

| Option | Description |
|--------|-------------|
| `--mount` | Mount the repository first if not already mounted |
| `--skip-router-restart` | Skip restarting the route server after the operation |

The execution sequence is:
1. Mount the LUKS-encrypted repository (if `--mount`)
2. Start the isolated Docker daemon
3. Auto-generate `.rediacc.json` from compose files
4. Run `up()` in all Rediaccfiles (A-Z order)

## Stopping Services

```bash
rdc repo down --name my-app -m server-1
```

| Option | Description |
|--------|-------------|
| `--unmount` | Unmount the encrypted repository after stopping. If this does not take effect, use `rdc repo unmount` separately. |
| `--skip-router-restart` | Skip restarting the route server after the operation |

The execution sequence is:
1. Run `down()` in all Rediaccfiles (Z-A reverse order, best-effort)
2. Stop the isolated Docker daemon (if `--unmount`)
3. Unmount and close the LUKS-encrypted volume (if `--unmount`)

## Bulk Operations

Start or stop all repositories on a machine at once:

```bash
rdc repo up -m server-1
```

| Option | Description |
|--------|-------------|
| `--include-forks` | Include forked repositories |
| `--mount-only` | Only mount, don't start containers |
| `--dry-run` | Show what would be done |
| `--parallel` | Run operations in parallel |
| `--concurrency <n>` | Max concurrent operations (default: 3) |
| `--skip-router-restart` | Skip restarting the route server after the operation |

## Autostart on Boot

By default, repositories must be manually mounted and started after a server reboot. **Autostart** configures repositories to automatically mount, start Docker, and run Rediaccfile `up()` when the server boots.

### How It Works

When you enable autostart for a repository:

1. A 256-byte random LUKS keyfile is generated and added to the repository's LUKS slot 1 (slot 0 remains the user passphrase)
2. The keyfile is stored at `{datastore}/.credentials/keys/{guid}.key` with `0600` permissions (root-only)
3. A systemd service (`rediacc-autostart`) runs at boot to mount all enabled repositories and start their services

On shutdown, the service gracefully stops all services (Rediaccfile `down()`), stops Docker daemons, and closes LUKS volumes.

> **Security note:** Enabling autostart stores a LUKS keyfile on the server's disk. Anyone with root access to the server can mount the repository without the passphrase. Evaluate this based on your threat model.

### Enable

```bash
rdc repo autostart enable --name my-app -m server-1
```

You will be prompted for the repository passphrase.

### Enable All

```bash
rdc repo autostart enable -m server-1
```

### Disable

```bash
rdc repo autostart disable --name my-app -m server-1
```

This removes the keyfile and kills LUKS slot 1.

### Keyfile Refresh on Deploy

When autostart is enabled, `rdc repo up` validates the LUKS slot 1 keyfile.
If the on-disk keyfile still matches the LUKS slot, no changes are made.

After transferring a repository between machines via `repo push` / `repo pull`,
the keyfile on the new machine won't match. In this case, `repo up` automatically
regenerates the keyfile and updates LUKS slot 1. You will see log messages:

```
Refreshing keyfile credential for <guid>
Killing LUKS slot 1: /mnt/rediacc/repositories/<guid>
Adding keyfile to LUKS slot 1: /mnt/rediacc/repositories/<guid>
```

This is safe, slot 0 (your passphrase) is never modified. If autostart is not
enabled, the check is silently skipped. Failures are non-fatal and do not block
the deploy.

### List Status

```bash
rdc repo autostart list -m server-1
```

## Complete Example

This deploys a web application with PostgreSQL, Redis, and an API server.

### 1. Set Up

```bash
curl -fsSL https://www.rediacc.com/install.sh | bash
rdc config init --name production --ssh-key ~/.ssh/id_ed25519
rdc config machine add --name prod-1 --ip 203.0.113.50 --user deploy
rdc config machine setup --name prod-1
rdc repo create --name webapp -m prod-1 --size 10G
```

### 2. Mount and Prepare

```bash
rdc repo mount --name webapp -m prod-1
```

### 3. Create Application Files

Inside the repository, create:

**docker-compose.yml:**

```yaml
services:
  postgres:
    image: postgres:16
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: webapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD: changeme
    command: -c listen_addresses=${POSTGRES_IP} -c port=5432

  redis:
    image: redis:7-alpine
    command: redis-server --bind ${REDIS_IP} --port 6379

  api:
    image: myregistry/api:latest
    environment:
      DATABASE_URL: postgresql://app:changeme@${POSTGRES_IP}:5432/webapp
      REDIS_URL: redis://${REDIS_IP}:6379
      LISTEN_ADDR: ${API_IP}:8080
```

**Rediaccfile:**

```bash
#!/bin/bash

up() {
    mkdir -p data/postgres
    renet compose -- up -d

    echo "Waiting for PostgreSQL..."
    for i in $(seq 1 30); do
        if renet compose -- exec postgres pg_isready -q 2>/dev/null; then
            echo "PostgreSQL is ready."
            return 0
        fi
        sleep 1
    done
    echo "Warning: PostgreSQL did not become ready within 30 seconds."
}

down() {
    renet compose -- down
}
```

### 4. Start

```bash
rdc repo up --name webapp -m prod-1
```

### 5. Enable Autostart

```bash
rdc repo autostart enable --name webapp -m prod-1
```