deps.edn is a configuration file used by the Clojure CLI (command-line interface) to manage project dependencies, classpath definitions, and build parameters. Written in EDN (Extensible Data Notation), a subset of Clojure data structures, it specifies library coordinates, local paths, and execution aliases. Unlike older build tools like Leiningen or Boot, deps.edn emphasizes simplicity and direct integration with Clojure's language features, making it the primary tool for dependency management in modern Clojure projects.

1 Overview and Purpose

1.1 Role in Clojure Ecosystem

The deps.edn file serves as the central configuration artifact for Clojure projects that use the official Clojure CLI. It declares external library dependencies, source code paths, and runtime configurations in a purely declarative data format. This approach shifts the paradigm from imperative build scripts to data-driven project definitions, allowing the CLI to compute a precise classpath deterministically.

1.2 Relation to Clojure CLI

The deps.edn file is consumed directly by the clj and clojure commands provided by the Clojure CLI toolchain. Upon invocation, the CLI reads the deps.edn file from the current working directory (and optionally from the user's home directory for global configuration). It uses the tools.deps.alpha library to resolve dependencies, fetch artifacts from Maven repositories or Git sources, compute the full Java classpath, and then launch the Clojure runtime.

1.3 Comparison with Leiningen and Boot

Featuredeps.ednLeiningen (project.clj)Boot (build.boot)
Configuration styleDeclarative data (EDN)Declarative DSL (Clojure)Imperative code (Clojure)
Plugin systemComposable aliasesLeiningen pluginsBoot tasks/pods
Dependency resolutiontools.deps.alpha (Maven/Git/local)Leiningen's built-in resolverMaven Aether
Learning curveLow (pure data)ModerateSteeper (code-based)

The key philosophical difference is that deps.edn treats configuration as inert data rather than executable code, simplifying reasoning about project structure and enabling better tooling support.

2 File Format and Syntax

2.1 EDN Basics

EDN (Extensible Data Notation) is a subset of Clojure's reader syntax. A deps.edn file is a single EDN map. Common EDN elements used in deps.edn include:

  • Keywords: :deps, :paths, :aliases
  • Symbols: org.clojure/clojure (used as keys in dependency maps)
  • Strings: "src", "1.11.1"
  • Vectors: ["src" "resources"]
  • Maps: {:mvn/version "1.11.1"}
  • Tagged literals: #profile {:test {:extra-paths ["test"]}}

2.2 Standard Keys

The root deps.edn map supports several standard keys, each with a specific purpose.

2.2.1 :deps

The :deps key holds a map of dependency specifications. Each key is a fully qualified symbol representing a library coordinate (e.g., org.clojure/clojure). The corresponding value is a map containing version information and optional modifiers. Common keys within this map include:

  • :mvn/version — specifies a Maven version string.
  • :git/url — specifies a Git repository URL.
  • :local/root — specifies a path to a local project.
  • :exclusions — a vector of library coordinates to exclude from transitive resolution.

2.2.2 :paths

The :paths key is a vector of directory paths (relative to the project root) that will be included in the Java classpath. If not specified, the default value is ["src"]. Resources, configuration files, or compiled classes are often added via this key.

2.2.3 :aliases

The :aliases key is a map where each key is an alias name (a keyword) and each value is a configuration map. When the alias is activated on the command line (e.g., clojure -M:test), its configuration map is merged with the base configuration. The alias map can contain any of the standard deps.edn keys.

2.2.3.1 :extra-deps

Dependencies that are only applied when the alias is active. Commonly used for test frameworks (e.g., lambdaisland/kaocha), development tools (e.g., nrepl/nrepl), or build utilities (e.g., seancorfield/depstar).

2.2.3.2 :extra-paths

Additional classpath directories active only under the alias. The most common use case is adding "test" to the classpath for running tests.

2.2.3.3 :main-opts

Arguments passed to the -main function when using the -M execution mode. This is typically used to specify the main namespace and any command-line arguments for application entry points.

2.2.4 :mvn/repos

The :mvn/repos key is a map of repository name keywords to repository URL strings. It allows users to define custom Maven repositories (internal corporate repositories, snapshot repositories, or alternative mirrors). The default Maven Central repository is always available unless explicitly overridden.

3 Dependency Specification

3.1 Maven Coordinates

Maven coordinates are the standard way to specify dependencies from Maven repositories. The format is:

group-id/artifact-id {:mvn/version "1.0.0"}

For example:

org.clojure/clojure {:mvn/version "1.11.1"}
com.google.guava/guava {:mvn/version "32.1.2-jre"}

The version string follows Maven versioning conventions and can include version ranges, SNAPSHOT indicators, or release markers.

3.2 Git Coordinates

Git coordinates allow direct inclusion of dependencies from Git repositories. They are specified using the :git/url key, and must include at least one of :sha, :tag, or :branch to pin a specific version. The Clojure CLI will clone the repository, compute a classpath, and generate a dependency version based on the commit hash.

com.example/git-lib {:git/url "https://github.com/example/git-lib.git"
                     :sha "abc123def456"}

Using :tag provides a human-readable reference:

com.example/git-lib {:git/url "https://github.com/example/git-lib.git"
                     :tag "v1.0.0"}

3.3 Local Dependencies

Local dependencies point to other projects on the local filesystem using the :local/root key. The project at that path must itself contain a valid deps.edn file. The CLI reads the local project's configuration and merges its dependencies and paths into the main classpath.

com.example/local-lib {:local/root "../local-lib"}

This is particularly useful for monorepo development and cross-project testing.

3.4 Version Ranges and Overrides

Maven version ranges are supported in deps.edn and follow standard Maven syntax:

  • [1.0.0,2.0.0) — any version from 1.0.0 to less than 2.0.0
  • (,1.5.0] — any version less than or equal to 1.5.0

Dependency overrides are specified using the :override-deps key within an alias. This forces specific versions of transitive dependencies, helping to resolve conflicts:

:aliases {:resolve-conflicts {:override-deps {com.example/problem-lib {:mvn/version "2.0.0"}}}}

4 Project Structure and Classpath

4.1 Default Source Paths

When no :paths key is specified in deps.edn, the CLI defaults to including only the "src" directory in the classpath. This encourages a conventional project layout where source code resides in src/ and tests reside in test/ (the latter typically added via an alias).

4.2 Custom Path Configuration

Projects can customize their classpath by explicitly defining :paths. Common additions include:

  • "resources" — for configuration files, static assets, or other runtime files.
  • "target/classes" — for compiled Java classes.
  • "dev-resources" — for development-only resources.

The order of paths in the vector determines their precedence on the classpath.

4.3 Classpath Calculation

The CLI computes the final classpath through a multi-step process:

  1. Read the base deps.edn (project level).
  2. Resolve all direct and transitive dependencies using Maven/Git/local coordinates.
  3. Apply any active aliases (merging their configuration into the base).
  4. Combine all specified paths (base paths + extra paths from aliases).
  5. Add the resolved jar files to the classpath.
  6. Check for a checkouts directory and override any matching jars.
  7. Cache the computed classpath for reuse in subsequent invocations.

5 Aliases and Profiles

5.1 Defining Aliases

Aliases are defined in the :aliases map at the root level of deps.edn. Each alias is a keyword associated with a configuration map that can contain any of the standard deps.edn keys. Active aliases are specified on the command line using the -A flag (or implicitly via -M, -X, or -T modes).

:aliases {:dev {:extra-paths ["dev"]
                :extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}}}
          :test {:extra-paths ["test"]
                 :extra-deps {lambdaisland/kaocha {:mvn/version "1.78.0"}}}}

5.2 Common Use Cases

5.2.1 Testing Aliases

Testing aliases add test-specific dependencies and paths. A typical test alias includes a test runner and the test source directory:

:aliases {:test {:extra-paths ["test"]
                 :extra-deps {lambdaisland/kaocha {:mvn/version "1.78.0"}}
                 :main-opts ["-m" "kaocha.runner"]}}

5.2.2 Development Tools (Repl, REPL)

Development aliases configure tools that aid in interactive development, such as REPL clients, code hot-reloaders, or structural editors:

:aliases {:dev {:extra-paths ["dev"]
                :extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}
                             cider/piggieback {:mvn/version "0.5.3"}}
                :main-opts ["-m" "nrepl.cmdline"]}}

5.2.3 Build Aliases

Build aliases encapsulate tasks such as compiling, packaging, or deploying. These often use dedicated libraries like depstar for building uberjars:

:aliases {:uberjar {:extra-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
                    :main-opts ["-m" "clj-new.tools.build"]
                    :exec-fn clj-new.tools.build/uberjar}}

5.3 Merging and Resolution

When multiple aliases are activated (e.g., clojure -M:dev:test), the CLI merges their configurations using a deep merge strategy:

  • Maps: merged recursively (later aliases override earlier ones for duplicate keys).
  • Vectors: concatenated (order preserved).
  • Scalars: later values override earlier values.

This composability allows users to combine independent configurations without conflicts.

6 Execution and Commands

6.1 Using clj and clojure

The Clojure CLI provides two main executables: clj and clojure. The clj command is a wrapper around clojure that adds readline support (via rlwrap) for a better interactive REPL experience. Both commands accept similar arguments and read deps.edn from the current working directory.

6.1.1 -Sdeps Flag

The -Sdeps flag allows users to specify an alternative deps configuration by providing a string of EDN data that gets merged with the project's deps.edn:

clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version "1.0.0"}}}' -M -m nrepl.cmdline

This is useful for ad-hoc dependencies or temporary configurations.

6.1.2 -M and -X Flags

The -M flag runs a -main function specified by :main-opts in an alias or directly on the command line:

clojure -M:test

The -X flag invokes a Clojure function directly with a hash-map of arguments, using the :exec-fn key from an alias:

clojure -X:build uberjar

This mode encourages a functional approach to build scripts, where build steps are just Clojure functions.

6.2 Running Tests

The typical command to run tests is:

clojure -M:test

The :test alias normally specifies the test runner as the main entry point. For example, using Kaocha:

clojure -M:test

The exact command depends on the alias configuration; some projects use different test runners like cognitect/test-runner.

6.3 Launching a REPL

To launch a REPL with the full project classpath:

clj

For a customized REPL environment with development tools:

clojure -M:dev:repl

This activates the :dev and :repl aliases, adding their extra dependencies and paths before entering the REPL.

7 Advanced Topics

7.1 Coordinated Projects (Monorepos)

For monorepo setups or projects with multiple interdependent libraries, deps.edn supports coordinated development through two features:

  • :local/root: allows a project to depend on another local project by referencing its directory. The CLI reads the local project's deps.edn and merges its classpath.
  • checkouts directory: a top-level directory named checkouts can contain symbolic links to local projects. The CLI automatically finds these and overrides any resolved jar dependencies, enabling seamless cross-project testing without modifying deps.edn.

7.2 Integration with Build Tools (tools.deps, deps.clj)

The tools.deps.alpha library is the underlying dependency resolver used by the official CLI. It is also available as a standalone library for use in custom build tools. Third-party alternatives like deps.clj provide polyglot compatibility (e.g., for scripting languages that need to invoke Clojure). These tools all parse the same deps.edn format, ensuring interoperability.

7.3 Lockfile and Reproducible Builds

The CLI can generate a lockfile (deps.edn.lock) that records the exact resolved versions of all transitive dependencies. This lockfile can be committed to version control, ensuring that every build uses the same dependency versions regardless of when it runs or on which machine. To generate or update the lockfile:

clojure -X:deps lock

Once a lockfile is present, the CLI uses it instead of resolving dependencies from scratch, guaranteeing reproducible builds.

8 Practical Examples

8.1 Minimal deps.edn for a Library

{:deps {org.clojure/clojure {:mvn/version "1.11.1"}}}

This minimal configuration includes only the Clojure compiler and core libraries, defaulting to the src source path.

8.2 Full Project with Aliases

{:deps {org.clojure/clojure {:mvn/version "1.11.1"}
        com.google.guava/guava {:mvn/version "32.1.2-jre"}}
 :paths ["src" "resources"]
 :mvn/repos {:central {:url "https://repo1.maven.org/maven2"}
             :clojars {:url "https://repo.clojars.org"}}
 :aliases {:test {:extra-paths ["test"]
                  :extra-deps {lambdaisland/kaocha {:mvn/version "1.78.0"}}
                  :main-opts ["-m" "kaocha.runner"]}
           :dev {:extra-paths ["dev"]
                 :extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}}}
           :build {:extra-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
                   :exec-fn hf.depstar/uberjar}}}

8.3 Managing Multiple Environments

Projects often need distinct configurations for development, staging, and production. This is achieved through composable aliases:

:aliases {:base {:paths ["config"]}
          :dev {:extra-deps [dev-only-deps...]}
          :prod {:extra-paths ["config/prod"]}
          :staging {:extra-paths ["config/staging"]}}

Activating :dev and :base together gives a development environment, while :prod and :base gives a production environment.

9 Troubleshooting and Common Pitfalls

9.1 Dependency Conflicts

When multiple dependencies require different versions of the same library, the CLI resolves by default to the highest version. If conflicts cause errors (e.g., incompatible bytecode or missing methods), users can:

  • Use :override-deps in an alias to force a specific version.
  • Inspect the dependency tree with clojure -Stree (or -Sforest).
  • Examine the resolved classpath with clojure -Spath.

9.2 Missing Repositories

Errors indicating unavailability of artifacts usually point to missing repository configurations. Common causes include:

  • Forgetting to add Clojars (https://repo.clojars.org) when using non-Maven-Central libraries.
  • Using a corporate repository without configuring it in :mvn/repos.
  • Network connectivity issues or incorrect repository URLs.

9.3 Path Resolution Errors

Classpath errors often stem from incorrect path specifications:

  • The flycheck-mode is an Emacs term; applying similar logic: path errors manifest as class-not-found exceptions or missing resources.
  • Ensure :paths entries are relative and that directories actually exist.
  • Verify that :local/root paths point to valid projects with their own deps.edn.

10 Future Directions and Community Adoption

The deps.edn format and the Clojure CLI have gained widespread adoption within the Clojure community, particularly among new projects and those requiring reproducible builds. The Clojure core team continues to develop tools.deps.alpha, with ongoing improvements to:

  • Lockfile reproducibility and performance.
  • Integration with modern CI/CD pipelines.
  • Support for GraalVM native-image compilation.
  • Enhanced aliasing and profile composition mechanisms.

The ecosystem has largely converged on deps.edn as the standard for dependency management, with major libraries, frameworks, and tools providing first-class support for the format. While Leiningen remains in use for legacy projects, deps.edn represents the present and future of Clojure project configuration.