Explore guides

Machines, files, and configuration

Compose a typed setup for multiple machines with explicit paths, overrides, and ownership.

5 minute readSource checked: October 5, 2026Complete source guide

Compose ordinary TypeScript modules

Configurations contain objects, synchronous context factories, and nested arrays. Keep shared resources together, then add platform and machine fragments. Split them into imported modules as the setup grows.

TypeScript
import { defineConfig, darwin, linux, machine, tools } from "@dovocode/workstation";

export default defineConfig([
  { resources: [tools.mise({ node: "lts" })] },
  darwin({ resources: [tools.brewCask(["ghostty"])] }),
  linux({ resources: [tools.apt(["git", "jq"])] }),
  machine(["studio", "macbook"], {
    resources: [tools.mise(["shfmt"])],
  }),
]);

Know what a path is relative to

Relative source paths resolve against the entry-point configuration directory. Relative file targets resolve against home. Tilde expands to home, absolute paths stay absolute, and imported fragments share the entry point’s base directory.

Machine selection uses the OS hostname before the first dot or the exact --machine override. The override changes the selector, not the OS or home directory.

Make overrides and dependencies deliberate

Declarations run in order. The last declaration with the same resource ID wins; objects are not deep-merged. Files use their resolved target path as identity. Packages and services have backend-specific identities.

Use dependsOn for explicit prerequisites. Read IDs from plan or status. Unknown dependencies and cycles fail before applying resources.

Keep the setup identity stable

Local state is namespaced by configuration path and machine. A stable configuration id makes a new setup independent of its entry-point path. Changing that ID or moving an existing setup changes the default state namespace.