When you’re developing web apps they tend to start out simple and then grow more and more complicated. On your servers you have services for your dependencies, but when you’re in development mode, it can be tricky to get everything running at the right time. The tool I’ll show you here works for Node.js, PHP, Java, Ruby, Python and more.
I use a laptop as my main computer and in order to save battery life, I limit the number of services I have running. A common app may be built with Node.js and rely on MongoDB and Redis. Therefore I launch MongoDB, launch Redis, then when all are running, launch my Node app. Then when I’m done I have to remember to shut each down. It’s a chore – exactly the kind of chore computers are great at, so let’s have the computer do it for us.
I first learned about Foreman when publishing apps to Heroku. It’s deeply integrated into their documentation and deployment process. It’s a free open source source tool originally announced on the website of David Dollar and the documentation is provided in the form of an inadequate man page. (it would be better if it included documentation of the Procfile)
There are three steps:
- Define your services in a Procfile
- Specify any environment variables in a .env file
- Use the Foreman command to launch your app and it’s services or any subset
When you’re done, hit ctrl+c in the console and they all exit.
Here’s my Procfile for an app that uses Node.js, MongoDB and Redis:
web: node app.js db: mongod --dbpath=.db/mongo redis: redis-server /usr/local/etc/redis.conf --dir $PWD/.db/redis
Note that in each case I’ve listed a name with a colon and then the command line to launch the service. That’s really all there is to it, but I also wanted to point out two other things: environment variables and the commands I use to keep track of transient files.
If you notice the db line, I like to set the database path for my database to be in the project folder. I usually add .db to my .gitignore file so that they don’t get checked in. By putting the database in my working directory then I can use this technique on multiple apps locally and not have their data affect each other.
With MongoDB you simply use the –dbpath command and it accepts paths relative to where they’re launched from. (If you know how to do this with MySQL or MariaDB, please let me know in the comments, I’d love to use this feature with those databases).
Redis doesn’t accept relative paths, therefore I have to use an environment variable. In this case, the PWD variable is a standard variable available to the local environment.
I can also specify custom environment variables in the .env file. It is customary to not check .env files into your version control, but you can if you like. This is a great place to put your social networking or OAuth secrets, for example. You simplify specify a variable name followed by an equal sign and then the value, exactly like you would in a terminal:
Once you’ve specified what you like, you can launch all of your commands at once, with
foreman start. If you’d like to have multiple Procfiles you can specify the name of the file as the last parameter:
foreman start -f Procfile.dev. This is handy in cases where I use Supervisor to start my Node.js app in development mode, which I wouldn’t want to do on production. Likewise, you can specify one or more .env files like this:
foreman start -e .env,.envdev.
One fun thing you can do is use Foreman to launch multiple web servers, each on different ports. For example, let’s say I want to test concurrency with my Node.js app. I can specify a command like this:
foreman start -c web=2,db=1,redis=1. Note that if I list a service then only that service will be started, so if you want them all you have to list them all. When I do this, I’ll see output like this:
10:06:02 web.2 | Express server listening on port 5001 10:06:02 web.1 | Express server listening on port 5000
This means that there are two running, one on 5000 and the other on 5001. Therefore I’d view the app in my browser at http://localhost:5000 or http://localhost:5001.
I hope this helps you simplify your development, if you have any suggestions or questions, feel free to comment!