It’s a development team’s dream to be able to take a fresh machine, pull some code, and be up and running within a few minutes without having to install a host of dependencies.
We think that we’re pretty close to that dream with Vito.
A few years ago, I decided to try Docker-izing Tito for local development. To cut a long story short, it didn't go well. At the time, around 2017, Docker filesystem sync on a Mac was slow and required a separate process to copy files from the local machine to the Docker image.
We did eventually get things running with Mutagen, but even since then, the filesystem syncing on Docker for Mac has greatly improved, firstly with gPRC Fuse, and lately with virtioFS. Coupled with the performance of M1 Macs, developing web apps with Docker these days is pretty close to native performance.
So, how does it work?
When you want to get up and running with Vito on a new machine (assuming you’ve used something like Strap to install a generic development environment), here’s what you do:
Clone the Vito repo
docker-compose build to build all of the images
docker-compose run vito bash -c "yarn && rails db:create && rails db:schema:load && rails db:seed to create, populate and load test data into the database.
./bin/up-all to run the app and webpack-dev-server for asset recompilation with hot module replacement.
Now you have a local copy of Vito running at https://dev.vito.community (this is pointed via DNS to 127.0.0.1). You get fully working SSL with a LetsEncrypt cert. You get a local S3 server for file uploads (via localstack). You don't need to install postgres locally. You don't need to configure nginx. You don't need to modify anything else. Redis and Memcached are installed and running.
It is a near-production, reproducible development environment. At any time, or, for example, switching between branches with migrations, you can run bin/reset_database to get a completely fresh copy of the seeded database, shared by all developers.
It's really, really nice.
If you wanted to get started with your own app, the Evil Martians blog has a great resource on getting up and running, and indeed we hired them to help us with our setup. Here’s a list of the services we use and what they do:
I chose Traefik for exposing the app via a URL in development. It provides a few things that make it very compelling. Traefik does automatic service discovery via docker-compose labels and it just works. Additionally, it integrates with DNS providers (we use Route 53 and DNSimple) to create and renew a local SSL certificate so that we have a real cert terminated at https://dev.vito.community
We use Docker images for postgres, memcached and redis, making them super easy to configure in rails since Docker gives them automatic hostnames based on the service name in docker-compose.yml
Localstack provides a huge range of local cloud services, but we use it for S3. Imgproxy is a self-hosted proxy that allows resizing and manipulation of images without a third party. Mailhog is a fast, simple local SMTP server that shows you what emails sent from your app look like. Via traefik, we have it running at https://mailhog.dev.vito.community
We use Anycable for Websockets (Martians wrote about it here). Then we have a series of services that all depend on a vito-backend service, as per the Martians blog post above. Because each service specifies its dependencies, it's one command not just to start the development server, but also all of its dependencies. And even better, when you stop the service, you’re just a docker-compose down away from having everything stopped.
So that’s our setup. It has served us really well for nearly 2 years now, and while I feel that there are other choices that we could have made along the way to simplify things elsewhere, it feels like the added work getting this all up and running in development is worth it for how easy it is to pick up the code, run the app, commit some changes, and go.