Scalable infrastructure with Larvel Forge and Digital Ocean

We’re getting close to the public launch for Upfocus. In order to accommodate (hopefully) growing usage, I really wanted to make sure we had scalable infrastructure in place: ability to add more server capacity, atomic deploys, etc.

After a bit of research and experimentation, I’m really happy with the Laravel Forge + Digital Ocean + DeployHQ solution I’ve put together. It was way easier than I thought it would be.

Web nodes

We’re using Laravel Forge to manage two Digital Ocean droplets. These droplets sit behind Digital Ocean’s load balancer product, and the load balancer sits behind CloudFlare.

Forge’s specific advantages are:

  • All of the software necessary for Laravel is installed for you.
  • There’s a nice admin panel for managing scheduled jobs, queues, and other Laravel-specific configuration.

Originally, I tested out Digital Ocean’s new App Platform. However, deployments took too long for my liking (~6-10 minutes). There were a few other things I ran into that gave me pause, too. For instance, you have to provision another container for the queue, and it’s unclear as to whether a deploy will blow away a job in progress.

SSL in front of the load balancer was a bit tricky. Digital Ocean can’t provision a LetsEncrypt certificate unless you also manage your DNS with Digital Ocean. Fortunately, it’s possible to install CloudFlare’s origin certificate on the load balancer, and then use CloudFlare for SSL.


For atomic deploys, DeployHQ ended up working way better than I expected. It’s pretty much magic; they’ve done a really good job with their product.

Here’s how it works:

  1. GitHub sends a ping to DeployHQ on a push to master.
  2. DeployHQ fetches the latest, runs npm run prod and composer install, and then syncs only the changed files to a release folder on the server. The rest of the release folder is copied directly on the server from the previous release.
  3. DeployHQ syncs the .env file to the release folder on each server.
  4. After the release is ready, DeployHQ updates the symlink to point to the new release.

That’s it! Seriously, I’m very impressed with how well it works.


Because I want to manage as few servers as possible, we’re using Digital Ocean’s managed MySQL and Redis. For both of these products, system admin is easy peasy lemon squeezy — nothing. I also get easy access to logs and, if we need to add more capacity, I can easily add replicas.

For uploads, we’re using Digital Ocean Spaces: 250GB storage and 1 TB outbound for only $5/month. It seems like too good of a deal.


In the future, if I need to add more web server capacity, I’ll simply:

  1. Provision another droplet with Laravel Forge.
  2. Once the droplet is live, start a deploy with DeployHQ. After the deploy is complete, set up the symlink from deploy to
  3. Grant access for the droplet to connect to MySQL and Redis.
  4. Configure the queue and scheduled jobs on the server.
  5. Add the server to the load balancer pool.

All in all, I think this setup should serve us well for a while. It seems like the right balance between power and simplicity.

Happy to take any questions in the comments or on Twitter!

Leave a Reply