# mount: making Clojure application state reloadable without losing the REPL

> mount is a Clojure and ClojureScript state management library that keeps application state in top-level vars with start and stop functions, so a running app can be restarted from the same REPL session. It suits Clojure teams whose workflow depends on a live REPL and who want state to be reloadable rather than rebuilt.

**tolitius/mount** — managing Clojure and ClojureScript app state since (reset)

- Repository: https://github.com/tolitius/mount
- Stars: 1,255 · Forks: 88
- Language: Clojure
- License: EPL-1.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/tolitius-mount

## The problem is REPL startup time, not JVM startup time

mount's argument starts from a constraint that Clojure developers feel daily. The Clojure REPL, whether started with lein repl or boot repl, does not fire up instantly, and the README is explicit that this is not because of the JVM, linking instead to a breakdown of Clojure REPL startup time and a separate piece on JVM slow startup.

The consequence is that restarting the REPL to pick up a change throws away your application state, and with it the live connections, queues and background threads that state represents. The README's framing is that depending on how application state is managed during development, the three Clojure superpowers of power, simplicity and fun can either stay, go somewhat, or go completely.

So the requirement is narrow and specific: make the application state reloadable inside the same REPL session, so the REPL's value survives a code change. The library describes itself as managing Clojure and ClojureScript app state since reset, and the project's stated goal is to preserve all the Clojure superpowers while making the application state enjoyably reloadable.

The audience follows. This is a development-workflow library for people who live in a REPL, not a general application architecture. It also carries an explicit invitation to contribute, with a Clojurians Slack channel and an issue tracker, which tells you the audience is a community as much as a user base.

## A state is a top-level var with a start function

The whole model fits in a few lines. You require the macro and define a state:

```clojure
(require '[mount.core :refer [defstate]])
```

```clojure
(defstate conn :start (create-conn))
```

The create-conn function is yours, it creates a connection, for example to a database, and the README notes it can be defined elsewhere or right above the state. If the resource needs to be cleaned up between reloads, there is a :stop key as well:

```clojure
(defstate conn :start (create-conn)
               :stop (disconnect conn))
```

That is the entire API for creating state. The important consequence is what the state becomes. The README calls it a top level being, which means it is an ordinary namespace-level var that other namespaces can require directly:

```clojure
dev=> (require '[app.db :refer [conn]])
nil
dev=> conn
#object[datomic.peer.LocalConnection 0x1661a4eb "datomic.peer.LocalConnection@1661a4eb"]
```

The value is bound in the var, so it is reachable from the REPL and from any namespace that requires it. The lifecycle functions are what mount calls when the application starts and stops, and the value in between is what the rest of the code reads.

Documentation strings work as they do on any definition, and the README shows the result of doc on such a state, which is a small sign that mount tries to behave like ordinary Clojure rather than as a special DSL.

## Dependencies are expressed with require, not with a component graph

This is the design choice that separates mount from libraries that require whole application buy-in, and the README argues it directly.

If a state is a simple top level being, the README's argument goes, these beings can coexist with each other and with other namespaces by being required instead. And if a stateful thing is kept lean and low level, such as I/O, queues, threads and connections, then dependency graphs stay simple and small, and everything else is just namespaces and functions, which the README calls the way it should be.

The comparison drawn in the Dependencies section is that a library requiring a whole app buy-in, where everything is a bean or a component, is a framework, and its dependency graph is usually large and complex because it contains every piece of the application.

Direct dependencies are still respected, and the README shows the pattern where one state requires another:

```clojure
: (ns app.config
  (:require [mount.core :refer [defstate]]))

(defstate config
  :start (load-config "test/resources/config.edn"))
```

and then a second state that uses it:

```clojure
(ns app.database
  (:require [mount.core :refer [defstate]]
            [app.config :refer [config]]))

(defstate conn :start (create-connection config))
```

The order of the start functions follows the require graph, which is ordinary Clojure dependency ordering rather than a separate declaration. The load-config function in that example comes from the author's own cprop library, and the README notes it could equally be a custom function that loads configuration from a file.

## The alternative mount names is Component

The README does not pretend to be the only option. It positions mount as an alternative to the component approach, with a dedicated document at doc/differences-from-component.md and a section of the same name.

The difference in approach is visible from the rest of the README. Component treats the application as a graph of components that are started and stopped in dependency order, where the dependency structure is declared in the system. mount keeps states as ordinary top-level vars in ordinary namespaces, and the dependency structure is expressed by require, so the state graph and the code graph are the same graph.

That difference has consequences in both directions. With component, the system is explicit, and you can start and stop parts of the application as units because the units are declared. With mount, the states are small and independent, and the README's own table of contents lists sections on starting and stopping parts of an application, starting an application without certain states, and stopping an application except certain states, which suggests the same fine-grained control is available without a declared system.

The README also has a section titled Value of Values, under the claim that lifecycle functions start and stop can take both functions and values. That is a real difference in ergonomics: a state whose start is a plain value does not need a function at all, which removes a layer of ceremony for states that are just configuration or a constant.

For an evaluator, the honest way to decide is to read doc/differences-from-component.md, since the library is explicit that the two approaches disagree about where the structure of the application should live.

## The documented feature set is wide, and the table of contents says so

The README is long, and its generated table of contents is a fair map of what the library covers, because every heading corresponds to a documented capability.

On lifecycle control there are sections on start and stop order, composing states, starting and stopping parts of an application, starting an application without certain states, and stopping an application except certain states. On changing behaviour at runtime there are sections on swapping alternate implementations, and on swapping states with values and with states, which are two different mechanisms for the same problem.

On the code-reload side there are sections on affected states, recompiling namespaces with running states, the :on-reload hook, and cleaning up deleted states. That last one addresses a real hazard: when you delete a state definition during a reload, something has to decide what happens to the resource it was holding.

On diagnostics there are sections on logging, with subsections for mount-up and manual AOP, and on exception handling. On language coverage there is a section asserting that ClojureScript is Clojure, a separate document at doc/clojurescript.md for managing state in ClojureScript, a section on cljc mode for sharing state code between the two, and a subsection on disabling lazy start.

Two further areas are worth flagging. Packaging has its own section, and the README has a section titled Mount and Develop! with a subsection on running the New York Stock Exchange, plus a further subsection on New York Stock Exchange maintenance, which is an unusually concrete demonstration for a library README. There is also a Web and Uberjar section, and a Runtime Arguments section for how the application reads its arguments at start.

If you are evaluating the library, that table of contents is the specification. The generated list is produced with DocToc, so it cannot drift from the headings in the document.

## The build supports deps.edn, project.clj and boot at once

The repository root shows three build worlds coexisting: deps.edn, project.clj and build.boot, alongside boot.properties, pom.xml, a package.json, a Makefile, src/, test/, dev/, doc/ and resources/.

The Makefile is the quickest way to see the intended commands. The test target runs the Clojure test suite:

```bash
clojure -X:test :patterns '[".*"]'
```

and the repl target starts a developer REPL with the dev and repl aliases:

```bash
clojure -A:dev -A:repl
```

Other targets cover cleaning, building a jar with clojure -A:jar, checking outdated dependencies with clojure -M:outdated, installing and deploying, and printing a dependency tree with mvn dependency:tree.

The Makefile also carries commented-out lines for ClojureScript testing, including a note to run a boot-based command until running ClojureScript tests via deps.edn is fixed. That is a useful detail for anyone setting up the project: the ClojureScript test path through deps.edn was not working at the time of writing, and the boot path is the one the maintainers used.

The package.json is a wrapper rather than a Node project. It publishes under the name @tolitius/mount, version 0.1.16, licence EPL-1.0, with the source under src/* and a single runtime dependency, ws. The repository also has a .clj-kondo/ directory, which is the linter configuration, and a .travis.yml for the older continuous integration setup.

## Maintenance, licensing and the practical limit of a global

The repository is not archived and the last push was on 2026-06-11. There are no GitHub releases, so there is no tagged version to pin and no release notes beyond the CHANGELOG.md in the root. The Clojars badge in the README points at the published artifact rather than at a GitHub release, and the community channel is the Clojurians Slack channel with the #mount message, plus the issue tracker.

The licence is EPL-1.0, recorded in package.json and in the LICENSE file, and the README has its own License section. EPL-1.0 is a weak copyleft licence, so a modified version distributed as a library carries obligations that MIT would not, and a team shipping a closed product that embeds mount would want to read the terms rather than assume MIT-like freedom. That is a factual note about the licence class, not legal advice.

The real limitation of the design is the one the README argues for. State is a global. The value lives in a top-level var, and the start and stop functions run against those globals, so two applications in the same JVM share them and a test that forgets to stop a state can leave a connection or a thread behind. The library's answer is the lifecycle discipline it provides, with :stop functions and the cleanup sections, but the underlying model remains global mutable state by design. That is a deliberate trade for reloadability, and a team that has decided global state is unacceptable should not adopt it regardless of how good the reload story is.

## Conclusion

Adopt mount if your team works in a live Clojure or ClojureScript REPL and wants to stop and start real resources such as connections, queues and threads without restarting the process, and if you are willing to model state as top-level vars rather than as a graph of components. Do not adopt it if you want an explicit dependency graph with framework-level buy-in, or if the weak copyleft of EPL-1.0 does not fit your distribution model. Before depending on it, check deps.edn and project.clj in the repository for the coordinate, read doc/differences-from-component.md, and note that the last push was on 2026-06-11 with no GitHub releases published.

## FAQ

### How do I define application state with mount?

Require the macro with (require '[mount.core :refer [defstate]]) and define a state with a :start key, for example (defstate conn :start (create-conn)). Add a :stop key when the resource needs to be cleaned up between reloads, such as (defstate conn :start (create-conn) :stop (disconnect conn)).

### How is mount different from Component?

The README positions mount as an alternative to the component approach and points to doc/differences-from-component.md for the details. Its states are ordinary top-level vars in ordinary namespaces, and dependencies between them are expressed with require, while a component style application is built as a declared system of components.

### Does mount work with ClojureScript?

The README has a section asserting that ClojureScript is Clojure and a separate document at doc/clojurescript.md on managing state in ClojureScript. It also documents a cljc mode for sharing state code between the two and a subsection on disabling lazy start.

### How do I run the mount test suite and start a REPL?

The Makefile test target runs clojure -X:test :patterns '[".*"]' and the repl target runs clojure -A:dev -A:repl. The commented ClojureScript lines in the Makefile note that running ClojureScript tests through deps.edn was not fixed at the time of writing, so a boot-based command is used instead.

### What licence does mount use?

EPL-1.0, recorded in the package metadata and in the LICENSE file at the repository root, with a License section in the README. The published artifact is on Clojars, and the repository publishes no GitHub releases.

## Sources

- [Issues](https://github.com/tolitius/mount/issues)
- [License: EPL-1.0](https://github.com/tolitius/mount/blob/master/LICENSE)
- [README](https://github.com/tolitius/mount/blob/master/README.md)
- [tolitius/mount on GitHub](https://github.com/tolitius/mount)

---

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