# smug: a tmux session manager driven by one YAML file

> smug is a Go program that creates tmux sessions, windows and panes from a YAML configuration file, with hooks for start, stop, attach and detach, git worktree aware session roots and per-window subsets you can start on demand. It sits between writing tmux commands by hand and the heavier configuration tools, and it depends on two Go modules in total.

**ivaaaan/smug** — Session manager and task runner for tmux. Start your development environment within one command.

- Repository: https://github.com/ivaaaan/smug
- Stars: 913 · Forks: 32
- Language: Go
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/ivaaaan-smug

## One file describes the whole terminal layout

smug automates your tmux workflow. You create a single configuration file and smug creates all the required windows and panes from it.

That is the entire idea, and the project is explicit that it is inspired by tmuxinator and tmuxp, the two tools that made declarative tmux configuration a habit. What distinguishes smug is less about the concept than about the surface area: the configuration is small, the command set is short, and the binary is written in Go with almost no dependencies.

The problem it solves is the one every developer with a real project hits. A working development setup usually means a server pane, an editor pane, a log pane, a database shell, and a couple of processes that have to be started in a particular order before anything works. Reproducing that by hand means either remembering a long sequence of tmux commands or keeping a shell script nobody can read. smug turns it into a file.

The description calls it a session manager and task runner, and the task runner part is visible in the configuration: hooks run commands at defined points in the session lifecycle, so bringing the environment up and tearing it down are part of the same file that describes the layout.

## Installing: go install is the route the project recommends

There are five install paths in the README, and the recommended one comes first under the Go heading.

```bash
go install github.com/ivaaaan/smug@latest
```

The prerequisites for that route are Git and Go, and the project notes that it tests with the last two major Go versions. Installing a Go program this way means the binary lands in your Go bin directory and is upgraded with the same toolchain you use for everything else.

The release archive route is spelled out step by step, because it involves moving the binary into a system location.

```bash
mkdir smug && tar -xzf smug_0.1.0_Darwin_x86_64.tar.gz -C ./smug && sudo mv smug/smug /usr/local/bin && rm -rf smug
```

The README asks you to replace the archive name with the one you downloaded. The packaging is handled by goreleaser, configured in a .goreleaser.yml file at the root, which is also what the release target in the Makefile drives.

For macOS there are two package managers.

```bash
brew install smug
```

```bash
sudo port selfupdate
sudo port install smug
```

And for Arch there is an AUR package, installed the usual way from a clone with makepkg.

```bash
git clone https://aur.archlinux.org/smug.git
cd smug
makepkg -si
```

One detail worth carrying over from the Go route: the project tests against the last two major Go versions, so a toolchain that is too old will not be supported.

## start, stop, new, edit and a set of short aliases

The usage line shows the shape of every command.

```bash
smug <command> [<project>] [-f, --file <file>] [--worktree <worktree>] [-w, --windows <window>]... [-a, --attach] [-d, --debug]
```

A project is created or edited in your editor, and then started or stopped.

```bash
smug new project
smug edit project
smug start project
smug stop project
```

Because the full words are long to type, smug ships aliases for most of the commands.

```bash
smug project # the same as "smug start project"
smug st project # the same as "smug stop project"
smug p ses # the same as "smug print ses"
```

The ability to act on part of a session is the feature that separates this from a plain configuration applier. You can start a single window by naming it after the project, or several windows at once, or use the repeated window flag, and you can stop a single window while leaving the rest of the session running.

```bash
smug start project:window1
smug start project:window1,window2
smug start project -w window1 -w window2
smug stop project:window1
```

That matters in practice. A window that runs a long build or a server can be left out of a start command when you only need the editor, and a crashed service window can be replaced without tearing down the session.

Other flags cover the rest. The attach flag forces the client to switch to the session, the inside-current-session flag creates the windows inside the session you are already in, and the detach flag detaches the session the way the same flag does in tmux itself. The debug flag prints every command smug issues to a log file under ~/.config/smug.

## Four lifecycle hooks do the starting and stopping for you

The session-level options are where the task runner part of the tool lives, and there are four of them plus one switch.

The attach option makes smug attach to the session after creation, and it defaults to false. The before_start option runs only before the session is created, and the stop option runs only before the session is killed. Those two are the obvious pair.

Then there are the two hooks that are easy to miss. The attach_hook runs every time the first client attaches to the session, and the detach_hook runs every time the last client detaches from it. The distinction from before_start and stop is the point: those fire once at the edges of the session's life, while the hook pair fires on attach and detach transitions, with the attach hook tied to the first client attaching.

In practice the pairing is what makes a shared session behave. Starting infrastructure when the first person arrives, and stopping it when the last person leaves, is exactly the behaviour that a service-oriented project wants, and it cannot be expressed with a single start command.

Because these are ordinary commands in the configuration, the tool stays out of the way: there is no plugin mechanism, no command syntax to learn, and the shell you already use is the runtime.

## Worktrees as session roots

One option deserves its own section because it changes how the tool fits into a modern development workflow.

If your project root is a git repository, the worktree flag starts the session in one of its worktrees instead. The worktree is matched by its branch name or by its directory name, and it overrides the root for the session.

```bash
smug start project --worktree feature-x
```

The reason this matters is that worktrees are how many teams run several copies of the same project at once, and each copy usually needs the same set of windows and panes. With a configuration keyed on the project, the worktree flag gives each copy the same layout in the right directory, without a second configuration file and without editing the root every time.

The implementation lives in its own file in the repository, worktree.go, with a matching test file, which suggests it is treated as a feature rather than an afterthought.

The other customisation mechanism is variable substitution. You use the ${variable_name} syntax in the configuration and then pass key and value arguments on the command line, which lets one configuration file serve several environments without being edited.

## The YAML that describes a session

Configuration files live in the ~/.config/smug directory in YAML format, named after the project, for example ~/.config/smug/your_project.yml. You may also create a file named .smug.yml in the current working directory, and it is used by default. A custom path is possible with the file flag.

```bash
smug start -f ./project.yml
```

Here is a full session definition, trimmed to the parts that matter.

```yaml
session: blog
root: ~/Developer/blog
env:
  FOO: BAR
before_start:
  - docker-compose -f my-microservices/docker-compose.yml up -d
stop:
  - docker stop $(docker ps -q)
```

The window and pane structure follows underneath, with a name, an optional root that can be relative to the session root, a layout, commands and panes.

```yaml
windows:
  - name: code
    root: blog
    manual: true
    layout: main-vertical
    commands:
      - docker-compose start
    panes:
      - type: horizontal
        root: .
        commands:
          - docker-compose exec php /bin/sh
```

Three attributes are worth noticing. The manual flag marks a window that you only want to start explicitly with the window flag, which is how a heavy or long-running pane stays out of the way. The selected attribute in the other example marks the window to focus at session start. And a pane carries its own type, which for the examples shown is horizontal, alongside its own root and commands, so a pane can run in a different directory from its window.

The other example in the README shows the attach option set to true at the session level, and an ssh command in a window, which is a common way to use this tool when the database is not on your machine.

## Two dependencies, a small Go tree, and a Claude Code skill

The dependency list is short enough to state in full.

```toml
module github.com/ivaaaan/smug

go 1.26

require (
	github.com/spf13/pflag v1.0.10
	gopkg.in/yaml.v3 v3.0.1
)
```

Two modules, one for flag parsing and one for YAML. For a tool that shells out to tmux, that is a deliberate choice, and it means the binary is small, the install is fast and the dependency advisories are not a problem.

The source layout matches the feature set. There are separate files for the command surface, commander.go, the configuration handling in config.go, the execution context in context.go, the option parsing in options.go, the tmux interaction in tmux.go, the worktree handling in worktree.go and the entry point in main.go, and most of them have a matching test file. There is a completion directory for shell completions and a man directory for manual pages, both of which matter for a command line tool that people use every day.

The Makefile is minimal and conventional, with a version target that reads the latest tag with git describe, a build target that stamps the version into the binary with ldflags and trims paths for reproducible builds, test and coverage targets, and a release target that requires GITHUB_TOKEN, creates an annotated tag, pushes it and runs goreleaser.

One recent addition is worth noting for anyone who dislikes writing YAML by hand. The repository ships a Claude Code skill that builds a new smug configuration interactively, so the file can be generated through a conversation instead of typed.

## Conclusion

Adopt smug when you rebuild the same development environment often enough that typing the tmux commands has become the slow part, and when you would rather describe a layout in a file you can commit than script it in shell. Do not adopt it if your tmux usage is one or two windows, because a YAML file is overhead in that case, and note that the tool is a thin layer over tmux rather than a replacement for it. Before the first run, write a config for one project and try it with smug start, keep the debug flag in mind because it logs every command it issues to ~/.config/smug/smug.log, and decide where your config files live, since the default is ~/.config/smug with .smug.yml in the current directory used automatically when it is present.

## FAQ

### How do I install smug?

The recommended route is go install github.com/ivaaaan/smug@latest, which needs Git and Go. There are also release archives you extract and move into /usr/local/bin, a Homebrew formula with brew install smug, MacPorts with port install smug, and an AUR package for Arch.

### Where does smug look for its configuration files?

Configuration files are stored in the ~/.config/smug directory in YAML format, named after the project. A file named .smug.yml in the current working directory is used by default, and the --file flag points at a custom path such as ./project.yml.

### What is the difference between before_start and the attach_hook in smug?

before_start runs only before the session is created and stop runs only before the session is killed. The attach_hook runs every time the first client attaches to the session, and the detach_hook runs every time the last client detaches from it.

### Can smug start only some of the windows in a session?

Yes. You can name a window after the project with smug start project:window1, list several with smug start project:window1,window2, or use the repeated window flag with smug start project -w window1 -w window2. A window marked manual in the configuration is started only that way.

### What does the --worktree flag do in smug?

If the project root is a git repository, --worktree starts the session in one of its worktrees instead, matched by branch name or directory name, and that worktree overrides the root for the session.

## Sources

- [Issues](https://github.com/ivaaaan/smug/issues)
- [ivaaaan/smug on GitHub](https://github.com/ivaaaan/smug)
- [License: MIT](https://github.com/ivaaaan/smug/blob/master/LICENSE)
- [README](https://github.com/ivaaaan/smug/blob/master/README.md)
- [Releases](https://github.com/ivaaaan/smug/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ivaaaan-smug
