Deploying multiple Clojure apps on one server using Reagent, Nginx, and Supervisor (Solved)

469 views
Skip to first unread message

Seth Archambault

unread,
Dec 23, 2016, 9:48:22 AM12/23/16
to Clojure
I'm posting this because right now someone is scrounging the internet for 3 hours to find this simple solution, I hope that they find this post sooner rather than later, and can move on to their next project!

TLDR

Goto /src/clj/myapp/server.clj

Edit "3000" to be whatever you want:

 (defn -main [& args]
   (let [port (Integer/parseInt (or (env :port) "3000"))]
     (run-jetty app {:port port :join? false})))

Now Here's the journey of a new user to find this information...

Let's say you installed Clojure with:

lein new reagent myapp

You built out your app, and everything went well! Time to deploy.

You may have stumbled upon this DigitalOcean guide, How To Deploy a Clojure Web Application on Ubuntu 14.04

It worked mostly, though your app wants to run on port 3000, not 5000. No problem, just changed it in the nginx config

    6 server { 
  7     listen 80;
  8     server_name www.myapp.us myapp.us;
  9     location / {
 10         proxy_pass http://127.0.0.1:3000;
 11         proxy_http_version 1.1;
 12         proxy_set_header Connection "";
 13         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 14         proxy_set_header Host $http_host;
 15         access_log /var/www/myapp/logs/myapp.access.log;
 16         error_log /var/www/myapp/logs/myapp.error.log;
 17     }
 18 }

The recommended /etc/supervisor/conf.d/myapp.conf file worked great. Your site is launched and all is well!

Okay so with that resounding success you decide you want to launch a second clojure app. 

First, if you're used to using php and nginx, you might be thinking you may be able to have your app run on the default port buy "look" for another url, maybe it's something you can easily change in the nginx config, or the supervisor config, or maybe the project.clj file. But a search on google seems to be focused on using separate port numbers:

How can I run multiple Ring apps on the same server? 

You don't necessarily think of your app as a "Ring" app - sure it's a library, but isn't this just Clojure? Anyways, this seems to be the solution, but how do we change the port? 

Searching google some more:

How to set ring port based on profile - something about profiles, not helpful 
You browser the rest of the front page of google and continue to get posts like this.

If you come from a mainstream language like PHP, you're thinking "Hmm, deploying multiple apps is an extremely basic task.. Why do I feel like I'm the first one to ever do it?"

Thinking that maybe it's just a standard Java thing, you start searching for "change java jar port". But these all yeild posts that recommend changing the port via commands like this:

java -jar jenkins.war --httpPort=9090

None of these will works, but you will have to wait 60 seconds after typing it in each time to find out.  
Hours have passed. For the first time in your life as a programmer, Google has actually failed you.You are all alone now.

Finally, you open the project in an editor you're experienced with - Sublime text - and search the whole project for port. 

You find the place to change the port at /src/clj/myapp/server.clj ! 

You are victorious, but you feel let down by Google, and frustrated that the solution was random flailing around.

Epilogue

I really love Clojure. This is the first time, in a bout 10 years I've been this excited about programming. Functional, lisp-like programming feels like an absolute revolution to someone who has only ever experienced, php, ruby, python, and javascript.

However, early on in my Clojure journey I've encountered two extremely basic problems where the solutions simply don't seem to be findable on Google! There really is this feeling like, am I the first person to have eve encountered this problem? Am I the only one who is coming to Clojure from building web apps using Laravel (An excellent framework, unfortunately written in php)

Maybe there aren't a whole lot of beginners, or maybe some of the beginner written material no longer applies to the new fancy ways to get started. This is concerning. As a newby I want to be able to jump in knowing that the foundation has already been built. As nice as it is to use a command like "lein new reagent myapp" - I don't want to do that if the result is I get a tree of code made for me that most of the community hasn't used yet. What's frustrating is that this folder structure isn't explicitly connected to the project.clj - like there's no way you could study the project.clj and determine that /src/clj/myapp/server.clj is used by anything. It just happens magicaly, the complexity is hidden...

Again, I'm really enjoying clojure so far, and I really agree with the creators vision, but I'm concerned whenever really easy tasks take forever to do and seem to have hidden complexity.

I'm interested in other peoples thoughts here. Have you felt in a similar way? What am I doing wrong? 

Thanks!
Seth

James Reeves

unread,
Dec 23, 2016, 11:01:28 AM12/23/16
to clo...@googlegroups.com
I think this is something a lot of web developers take for granted, so perhaps it isn't mentioned as much as it could be.

The usual convention in Linux environments is to set the port via the PORT environment variable. So on the command line it would be:

  PORT=3001 java -jar yourapp.jar

That's what this line does in your source code:

(Integer/parseInt (or (env :port) "3000"))

It looks for the "PORT" environment variable, and if it doesn't find it then it defaults to "3000". Then it converts the resulting string into an integer.

You may want to take a look at the Luminus deployment guide. Luminus is a commonly used project template, but the deployment docs apply to most Clojure web projects and cover a range of setups.

- James


--
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+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Seth Archambault

unread,
Dec 23, 2016, 11:56:19 AM12/23/16
to Clojure, ja...@booleanknot.com
:-O 
That just blew my mind - Thanks! :)

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Seth Archambault

unread,
Jan 2, 2017, 5:15:10 PM1/2/17
to Clojure, ja...@booleanknot.com
A new issue with this. I'm running on a Ubuntu server with 1000 gigs of ram - this has been enough in the past for dozens of php apps - however I'm finding with each jar I spin up on the server, it uses ~ 84mb each. At this rate it will become expensive to constantly create new apps. 

PHP's memory usage seems to be dictated by traffic, not by how many websites you have running, is Java's memory use different?

I am using supervisor + nginx to serve the Clojure Uberjars - is there anything I can do to make this more efficient / not increase with each new site? Is there any technology I should look into, or practices I should implement for this?

Being able to run many websites on one server is a critical issue for me... Anyone have any advice? Thanks!

William la Forge

unread,
Jan 2, 2017, 8:27:19 PM1/2/17
to Clojure, ja...@booleanknot.com
Seth, something seems amiss. 1,000 GB is 1,000,000 MB. At 84 mb per jar, you can spin up 11,904 jar files. Which is worse than only being able to run only dozens of PHP apps.

--b 

Seth Archambault

unread,
Jan 3, 2017, 5:16:14 PM1/3/17
to Clojure, ja...@booleanknot.com
Haha thanks for pointing that out - I mispoke - 1024 mb of ram - 1 gig of ram. Using a $10 a month Vultr account. 1000 gigs would be a tad expensive!

Sean Corfield

unread,
Jan 3, 2017, 7:12:54 PM1/3/17
to Clojure Mailing List

1GB is certainly pretty small for the JVM world, if you’re thinking of running multiple apps / sites as separate JVM processes.

 

However, there are several ways around that.

 

It’s common in the JVM world to have a single “web server” process load and run multiple “web applications”. A servlet container (Tomcat, Jetty, JBoss…) runs multiple apps each packaged as a WAR file (a zip file with some additional metadata).

 

Another option is to package multiple applications into one uberjar and start up multiple apps on different ports directly inside your own code, or you could use a single app with middleware that selects a different set of routes for each different domain / port / however you distinguish between your apps externally.

 

Bottom line: having each app as a separate uberjar, spinning up in a separate JVM isn’t the most scalable way of running multiple apps on a single server.

 

For comparison, at World Singles, we have about 100 sites running on (a cluster of instances of) a single web application under the hood. The domain being requested determines how the request is handled – in our case the skin and theme of each site, along with a lot of other metadata, is all dynamic and based on the domain name. Our sites are similar enough that this is possible. That’s for the main customer-facing sites. We also have an affiliate web site and an internal admin web site. Those three codebases are each, essentially, a WAR-based app and all three can run on a single Tomcat instance (on each server in the cluster). We run a single JVM with 10GB heap configured for Tomcat on each of a cluster of servers, each with 64GB RAM (our database servers are in a separate cluster and have 128GB RAM each, I believe).

 

Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

--

Seth Archambault

unread,
Jan 6, 2017, 7:01:17 PM1/6/17
to Clojure
Thanks! Sounds like multiple war files would be the right way for me. Unfortunately, I'm falling into the original problem with finding information on how to do this...

Got any links to articles on how to do this? 
Thanks!

Ray Miller

unread,
Jan 7, 2017, 5:19:19 AM1/7/17
to clo...@googlegroups.com
If you're using ring, you can use the lein-ring plugin to build the uberwar: simply `lein ring uberwar`. Check out the README for options if you need to deviate from the defaults: https://github.com/weavejester/lein-ring

You'll end up with an uberwar for each application you want to deploy. These have to be deployed to a servlet container; there are several options here, but jetty is a popular choice. This tutorial looks helpful: https://github.com/ddellacosta/Clojure-under-Jetty-and-Apache


For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages