> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://rivet.chrisboulton.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://rivet.chrisboulton.com/_mcp/server.

# Your first configuration

> Create a directory and a file, preview the change, apply it, and repair drift.

This walkthrough manages a directory and a file on one host. Run it on a disposable Ubuntu 24.04 machine or VM, because it makes real changes. To drive the host from a Mac or another machine instead, follow the same steps with `--target` in place of `--local`; see [SSH from a controller](/operations/ssh).

## Describe the desired state

Create `configuration/main.hcl`. The same file is in the repository at `examples/file/main.hcl`.

```hcl
file "directory" {
  path   = "/var/lib/rivet-example"
  ensure = "directory"
  mode   = "0755"
}

file "message" {
  path    = "/var/lib/rivet-example/message.txt"
  content = "managed by rivet\n"
  mode    = "0644"

  meta {
    depends_on = [file.directory]
  }
}
```

Each block is a resource: a type (`file`), a label you choose (`"message"`), and the properties you want. The label identifies the declaration; `path` identifies the object on the host. `depends_on` orders the directory first. Rivet would also infer that ordering here, because the directory is the file's parent.

## Collect facts and compile

```sh
umask 077
rivet facts --local > facts.json
rivet compile ./configuration --facts facts.json --out first.bundle
rivet graph first.bundle
```

```text
Compiled 2 resources, digest c076537d80561d9902e1ed74009f714988266267f22fa3d63f6550ef615a9eba
file.directory
file.message
file.directory -> file.message (DependsOn)
file.directory -> file.message (Expression)
```

`facts.json` describes this host. Compiling against it produces a bundle bound to this machine. `graph` lists resources in the order they will run and explains each edge. Compilation never overwrites an existing file, so use a new bundle name each time you compile.

## Preview and apply

```sh
rivet plan first.bundle --local
```

```text
WouldChange file.directory [ensure]
Deferred file.message [] Prerequisite changes have not been observed
```

The plan predicts that the directory will be created. It can't predict the file yet, because the directory it lives in doesn't exist, so the file is `Deferred`. Planning writes nothing.

```sh
sudo rivet apply first.bundle --local
```

```text
Changed file.directory [ensure]
Changed file.message [ensure]
```

Apply it again and nothing changes:

```text
Unchanged file.directory []
Unchanged file.message []
```

## Repair drift

Edit the file by hand, then apply the same bundle again:

```sh
echo drift | sudo tee /var/lib/rivet-example/message.txt
sudo rivet apply first.bundle --local
```

```text
Unchanged file.directory []
Changed file.message [content]
```

Rivet inspects each resource before acting, so it notices the edit and restores the content. Only declared properties are managed: if you removed `mode` from the file declaration, Rivet would stop managing the mode and leave whatever is there.

Add `--format json` to any of these commands for a [full report](/reference/plan-and-apply#json-report). Run `sudo rivet journal --local` to see unresolved work, which is `[]` after a clean run.

## Clean up

To remove what you created, set `ensure = "absent"` on both resources, remove `content`, `mode`, and the `meta` block, then compile a new bundle and apply it. Rivet removes the file before the directory. See [file](/resources/file) for removal rules.

Next, [manage an application](/getting-started/application) with a validated config file and a service that restarts when it changes.