deploying clojure/compojure web apps

3,182 views
Skip to first unread message

Josh Kamau

unread,
Jan 8, 2013, 6:38:06 PM1/8/13
to clo...@googlegroups.com
Hi There ;

I am new to clojure. My question is: Whats the common practice when it comes to deploying webapps built on compojure? running via lein ring server or creating a war file ?  I would also like to know which is the best way to put load configuration files  i.e using properties file... or using a reader to load .clj file with a hashmap def?

Kind regards.
Josh


Oleksandr Petrov

unread,
Jan 9, 2013, 5:24:28 AM1/9/13
to clo...@googlegroups.com
We're using that technique for both long-running server processes and small web applications, i've covered in a blog post some time ago: http://coffeenco.de/articles/how_to_deploy_clojure_code.html

Basically, create a jar and run it from a jar.

If you don't want to embed jetty, you can deploy to jetty container.
If you want to have uninterrupted deployments, use nginx and multiple sever socket backends, kill old app after new app's startup.

As reg. configuration, you can check out a little application I wrote on GitHub:
Here's a config file:
Here's a primitive way to read it:

There are also scripts for deployment and so on, kind of a complete setup: https://github.com/ifesdjeen/upload-challenge

Not sure how common our practice is, but we do it that way.

Have fun!


On Wed, Jan 9, 2013 at 12:38 AM, Josh Kamau <joshn...@gmail.com> wrote:

I am new to clojure. My question is: Whats the common practice when it comes to deploying webapps built on compojure? running via lein ring server or creating a war file ?  I would also like to know which is the best way to put load configuration files  i.e using properties file... or using a reader to load .clj file with a hashmap def?



--
alex p

Phil Hagelberg

unread,
Jan 9, 2013, 1:26:07 PM1/9/13
to clo...@googlegroups.com
I recommend simply embedding jetty and writing a -main function that
uses run-jetty. Then you can just ship an uberjar out to deploy. You can
also use `lein trampoline with-profile production run -m myapp.main` if
you take care that the dependencies are resolved once on your CI server
rather than each time the app is launched; see the end of `lein help
tutorial` for details about that.

You can generate a skeleton app using `lein new heroku myapp` that
contains the needed -main function even if you're not deploying on Heroku.

Another option is to generate an uberwar if you are familiar with the
ecosystem around .war files already. But if not it's easy to just use
jetty and treat a web application like you would any other Clojure app.

-Phil

John Gabriele

unread,
Jan 9, 2013, 3:58:16 PM1/9/13
to clo...@googlegroups.com
On Wednesday, January 9, 2013 1:26:07 PM UTC-5, Phil Hagelberg wrote:

I recommend simply embedding jetty and writing a -main function that
uses run-jetty. Then you can just ship an uberjar out to deploy.

Ok. Suppose I've got a stock webapp generated via

    lein new compojure my-webapp

I can of course (thanks to the nice template and lein-ring) run this sparkling new webapp via `lein ring server`.

In src/my_webapp/handler.clj I've got an ns declaration, a `(defroutes app-routes (GET ...) ... (route/not-found ...))`, and a `(def app (handler/site app-routes))`. That's it.

What would I need to do in order to embed jetty so I can create an uberjar (`lein uberjar`) and deploy it (I suppose via: `java -jar my-webapp-0.1.0-standalone.jar &`)?

What would need to be added to / changed in the project.clj?

What exactly should the `-main` look like?

Thanks!
---John

Tony Pitluga

unread,
Jan 9, 2013, 4:04:22 PM1/9/13
to clo...@googlegroups.com
I found this example: https://gist.github.com/raw/887596/d5804f1e7c550cd4c25e1194c58190e816ceadb0/core.clj


Just substitute your app-routes for handler and put the (run-jetty) form in your -main method.

Tony



--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Phil Hagelberg

unread,
Jan 9, 2013, 4:08:50 PM1/9/13
to clo...@googlegroups.com

John Gabriele writes:

> What would I need to do in order to embed jetty so I can create an uberjar
> (`lein uberjar`) and deploy it (I suppose via: `java -jar
> my-webapp-0.1.0-standalone.jar &`)?
>
> What would need to be added to / changed in the project.clj?
>
> What exactly should the `-main` look like?

All it takes is this in project.clj:

[ring/ring-jetty-adapter "1.1.6"]

and this in main.clj:

(defn -main [& [port]]
(let [port (Integer. (or port (System/getenv "PORT") 5000))]
(jetty/run-jetty #'app {:port port :join? false}))))

Of course adjust #'app to wrap with middleware appropriate for your app.
The batteries-included (drawbridge, wrap-error-page, cookie sessions)
template is here:

https://github.com/technomancy/lein-heroku/blob/master/lein-template/src/leiningen/new/heroku/web.clj

-Phil

Murphy McMahon

unread,
Jan 9, 2013, 4:16:47 PM1/9/13
to clo...@googlegroups.com
Thanks to technomancy for the very nice basic Clojure webapp template. Very cool.


John Gabriele

unread,
Jan 9, 2013, 4:27:57 PM1/9/13
to clo...@googlegroups.com
On Wednesday, January 9, 2013 4:08:50 PM UTC-5, Phil Hagelberg wrote:

John Gabriele writes:

> What would I need to do in order to embed jetty so I can create an uberjar
> (`lein uberjar`) and deploy it (I suppose via: `java -jar
> my-webapp-0.1.0-standalone.jar &`)?
>
> What would need to be added to / changed in the project.clj?
>
> What exactly should the `-main` look like?

All it takes is this in project.clj:

    [ring/ring-jetty-adapter "1.1.6"]

Ok. Added that to project.clj's :dependencies vector.
 

and this in main.clj:

    (defn -main [& [port]]
      (let [port (Integer. (or port (System/getenv "PORT") 5000))]
        (jetty/run-jetty #'app {:port port :join? false}))))


Ok. That also meant I had to add the bare `jetty` to the ns's :require.

Was able to create the standalone jar (`lein uberjar`), but could not run it ("Failed to load Main-Class"). Here's the project.clj:

~~~clojure
(defproject my-webapp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [compojure "1.1.3"]
                 [ring/ring-jetty-adapter "1.1.6"]]
  :plugins [[lein-ring "0.7.5"]]
  :ring {:handler my-webapp.handler/app}
  :profiles
  {:dev {:dependencies [[ring-mock "0.1.3"]]}})
~~~

I'm guessing there's a `:main` missing in there. What should its value be?

---John

John Gabriele

unread,
Jan 9, 2013, 4:39:06 PM1/9/13
to clo...@googlegroups.com
On Wednesday, January 9, 2013 4:27:57 PM UTC-5, John Gabriele wrote:
On Wednesday, January 9, 2013 4:08:50 PM UTC-5, Phil Hagelberg wrote:

and this in main.clj:

    (defn -main [& [port]]
      (let [port (Integer. (or port (System/getenv "PORT") 5000))]
        (jetty/run-jetty #'app {:port port :join? false}))))


Ok. That also meant I had to add the bare `jetty` to the ns's :require.

Wait. No. That can't be right.

The `ns` in src/my_webapp/handler.clj should probably be:

~~~clojure
 (ns my-webapp.handler
  (:use compojure.core)
  (:require [compojure.handler :as handler]
            [compojure.route :as route]
            [ring.adapter.jetty :as jetty]))
~~~

Still though, need to add a :main to project.clj I think.

John Gabriele

unread,
Jan 9, 2013, 4:51:51 PM1/9/13
to clo...@googlegroups.com
On Wednesday, January 9, 2013 4:39:06 PM UTC-5, John Gabriele wrote:

Still though, need to add a :main to project.clj I think.

BTW, tried `:main my-webapp.handler`. `lein uberjar` succeeds, but when I try to run the jar (via `java -jar my-webapp-0.1.0-SNAPSHOT-standalone.jar`), it fails with:

~~~
Exception in thread "main" java.lang.NoClassDefFoundError: my_webapp/handler
Caused by: java.lang.ClassNotFoundException: my_webapp.handler
    at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
Could not find the main class: my_webapp.handler. Program will exit.
~~~

Aaron Cohen

unread,
Jan 9, 2013, 5:03:49 PM1/9/13
to clo...@googlegroups.com
On Wed, Jan 9, 2013 at 4:27 PM, John Gabriele <jmg...@gmail.com> wrote:
On Wednesday, January 9, 2013 4:08:50 PM UTC-5, Phil Hagelberg wrote:

John Gabriele writes:  

and this in main.clj:

    (defn -main [& [port]]
      (let [port (Integer. (or port (System/getenv "PORT") 5000))]
        (jetty/run-jetty #'app {:port port :join? false}))))


What namespace is main.clj defining? It needs to have :gen-class added to it.

Minimally, it could be

(ns my-webapp.main
   [[some dependencies here]]
   :gen-class)
 
You could also move -main into my-webapp.handler (and add :gen-class to that ns declaration).


~~~clojure
(defproject my-webapp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [compojure "1.1.3"]
                 [ring/ring-jetty-adapter "1.1.6"]]
  :plugins [[lein-ring "0.7.5"]]
  :ring {:handler my-webapp.handler/app}
  :profiles
  {:dev {:dependencies [[ring-mock "0.1.3"]]}}

      :main my-webapp.main
 
)
~~~

I'm guessing there's a `:main` missing in there. What should its value be?

Yes, it needs to be whatever the name of the namespace is that contains your -main function. That namespace must have :gen-class in its definition.

John Gabriele

unread,
Jan 9, 2013, 5:21:31 PM1/9/13
to clo...@googlegroups.com
On Wednesday, January 9, 2013 5:03:49 PM UTC-5, Aaron Cohen wrote:
On Wed, Jan 9, 2013 at 4:27 PM, John Gabriele <jmg...@gmail.com> wrote:
On Wednesday, January 9, 2013 4:08:50 PM UTC-5, Phil Hagelberg wrote:

John Gabriele writes:  

and this in main.clj:

    (defn -main [& [port]]
      (let [port (Integer. (or port (System/getenv "PORT") 5000))]
        (jetty/run-jetty #'app {:port port :join? false}))))


What namespace is main.clj defining? It needs to have :gen-class added to it.

Minimally, it could be

(ns my-webapp.main
   [[some dependencies here]]
   :gen-class)

Ooof. Of course. It needed a `(:gen-class)` at the end of handler.clj's ns macro.

Works now. :) Thanks!

---John

Phil Hagelberg

unread,
Jan 9, 2013, 7:29:50 PM1/9/13
to clo...@googlegroups.com

John Gabriele writes:

> Ooof. Of course. It needed a `(:gen-class)` at the end of handler.clj's ns
> macro.

You don't actually need gen-class, you can do a fully-non-AOT'd uberjar
and just invoke it with -cp instead of -jar:

$ java -cp myproject-1.0.0-standalone.jar clojure.main -m myproject.main 8080

But it's not a bad idea to do a full AOT during builds just to catch
errors earlier and improve boot time.

-Phil

John Gabriele

unread,
Jan 9, 2013, 10:35:38 PM1/9/13
to clo...@googlegroups.com
Thanks, all. Updated CDS basic web dev tut with this deployment info.

---John

kinleyd

unread,
Jan 10, 2013, 12:48:37 AM1/10/13
to clo...@googlegroups.com
Yes, thank you all. This was a very useful thread.

Josh Kamau

unread,
Jan 10, 2013, 8:45:55 AM1/10/13
to clo...@googlegroups.com
Thanks for all the contribution in thread.

Josh


Reply all
Reply to author
Forward
0 new messages