Skip to content

Clojure Guide — Leiningen Profiles: Managing Configurations

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Clojure Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Leiningen profiles are named sets of configuration that can be merged into the main project map, enabling different configurations for development, testing, production, and other environments.

What You'll Learn

  • What profiles are and how they merge
  • Default profiles: dev, test, production
  • Custom profiles for specific tasks
  • Profile composition and ordering
  • Environment-specific dependencies

Why It Matters

Profiles prevent configuration duplication across environments. A single project.clj manages dev dependencies, test settings, and production deployment with clean separation. Durga Antivirus Pro uses profiles for build variants.

Real-World Use

Every Leiningen project uses profiles implicitly (dev, test, production) and explicitly (database config, deployment targets, CI settings).

flowchart LR
    A["Lein Profiles"] --> B["Default Profiles"]
    B --> C["Custom Profiles"]
    C --> D["Composition"]
    D --> E["Environment Config"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Default Profiles

Leiningen provides three default profiles:

;; project.clj
(defproject my-app "0.1.0"
  :dependencies [[org.clojure/clojure "1.11.1"]]

  ;; :dev profile is merged when using lein repl, lein run
  :profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]
                   :plugins [[lein-ring "0.12.6"]]
                   :source-paths ["dev"]}

             ;; :test merged when running lein test
             :test {:dependencies [[lambdaisland/kaocha "1.0.0"]]
                    :resource-paths ["test-resources"]}

             ;; :production merged with -with-profile
             :production {:jvm-opts ["-Xmx2g" "-server"]
                          :main my-app.core}})

Activating Profiles

;; With default profiles only
lein repl          ; Merges user, dev, and :default
lein test           ; Merges user, test, and :default

;; With specific profiles
lein with-profile +production run
lein with-profile +dev,+production repl

;; Override all profiles
lein with-profile production run

Custom Profiles

(defproject my-app "0.1.0"
  :profiles {:db-dev {:dependencies [[h2 "1.4.200"]]
                      :jvm-opts ["-Ddb.url=jdbc:h2:mem:dev"]}
             :db-prod {:dependencies [[postgresql "42.5.0"]]
                       :jvm-opts ["-Ddb.url=jdbc:postgresql://prod:5432/app"]}
             :ci {:plugins [[lein-cloverage "1.2.3"]]
                  :test-selectors {:all (constantly true)}}
             :uberjar {:aot :all
                       :main my-app.core}})

Profile Composition

;; Profiles merge recursively
;; Vectors: concatenated
;; Maps: merged (deeper keys override)
;; Scalars: latest wins

(defproject my-app "0.1.0"
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}
             :debug {:jvm-opts ["-Ddebug=true"]
                     :dependencies [[clj-debugger "1.0.0"]]}
             :dev-debug [:dev :debug]})  ; Composite profile

Profile Inheritance

;; Profiles can inherit other profiles
(defproject my-app "0.1.0"
  :profiles {:base {:dependencies [[ring "1.9.0"]]}
             :dev [:base {:dependencies [[ring/ring-mock "0.4.0"]]}]
             :test [:base {:resource-paths ["test/data"]}]})

Environment-Specific Config

;; Using profiles for environment configuration
(defproject my-app "0.1.0"
  :profiles {:dev {:env {:db-host "localhost"
                         :db-port "5432"
                         :log-level "debug"}}
             :staging {:env {:db-host "staging.example.com"
                             :log-level "info"}}
             :production {:env {:db-host "prod.example.com"
                                :log-level "warn"}}})

Excluding Profiles

;; Exclude certain profiles from merging
(defproject my-app "0.1.0"
  :profiles {:dev {:dependencies [[org.clojure/tools.namespace "1.3.0"]]}
             :repl {:plugins [[cider/cider-nrepl "0.28.0"]]}})

;; Run without default profiles
lein with-profile -dev,-user repl

;; Only specific profiles
lein with-profile production,uberjar uberjar

Plugins in Profiles

(defproject my-app "0.1.0"
  :profiles {:dev {:plugins [[lein-ring "0.12.6"]
                             [lein-refactor "0.0.5"]]}
             :test {:plugins [[lein-cloverage "1.2.3"]]}
             :repl {:plugins [[cider/cider-nrepl "0.28.0"]]}})

Common Mistakes

1. Profile explosion

Too many profiles make project.clj hard to maintain. Keep profiles focused on orthogonal concerns.

2. Order-dependent merging

Profile merge order matters. Understand that later profiles override earlier ones in vectors and scalars.

3. Committing development secrets

Never put production passwords in profiles. Use environment variables or external config files.

4. Forgetting :default profiles

The :default profile is always active unless explicitly excluded with -.

5. Profile not activating

If a profile doesn't take effect, check the merge order and whether another profile is overriding the key.

Practice Questions

1. What are Leiningen profiles? Named sets of configuration that merge into the project map, enabling different settings for different environments.

2. How does profile merging work? Vectors concatenate, maps merge recursively, and scalars are overridden by the last active profile.

3. What are the default profiles? :dev (active in repl/run), :test (active in test), :user (global), and :default (base config always active).

Challenge: Design profiles for a web application with dev, staging, production, and CI environments.

FAQ

{{< faq question="Can profiles change dependencies?" >}} Yes. Dependencies in profiles are added or merged. Use :dev dependencies for test frameworks and development tools. {{< /faq >}}

{{< faq question="How do I see the effective configuration?" >}} Run lein show-profiles to list profiles and lein with-profile +dev exec -e '(pprint (clojure.edn/read-string (slurp "project.clj")))'. {{< /faq >}}

{{< faq question="Are profiles evaluated lazily?" >}} Yes, profiles are merged at task execution time, not at project load time. This allows dynamic profile computation. {{< /faq >}}

{{< faq question="Can profiles use environment variables?" >}} Yes, use #=(System/getenv "VAR") in profiles.clj or read from environment at runtime. Never hardcode secrets. {{< /faq >}}

{{< faq question="What is profiles.clj?" >}} An optional file that is not committed to version control. It's ideal for local settings, credentials, and machine-specific paths. {{< /faq >}}

Mini Project

Create a Leiningen project with environment profiles:

(defproject blog-engine "0.1.0"
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [ring "1.9.0"]
                 [compojure "1.7.0"]
                 [hiccup "1.0.5"]]
  :profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]
                   :plugins [[lein-ring "0.12.6"]]
                   :ring {:handler blog.handler/app}}
             :prod {:jvm-opts ["-Xmx4g" "-server"]
                    :main blog.handler
                    :aot :all}
             :test {:dependencies [[lambdaisland/kaocha "1.0.0"]]
                    :test-selectors {:unit #(not (:integration %))
                                     :integration :integration}}})

What's Next

Now that you understand profiles, explore nREPL for interactive Clojure development.

Topic Description Link
Clojure nREPL Interactive development {{< ref "26-nrepl" >}}
Clojure ClojureScript Clojure on the browser {{< ref "27-clojurescript" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro