Deploying Like an Early Stage Startup
Co-founding a startup is hard. There’s lots of tasks to manage, you’re always running low on attention, time and hours in the week. If you’re lucky enough to have a team of three by now, with two people who are in charge of tech, it still won’t feel like you’re making enough progress on the product.
Of course you can’t just focus on building the thing. There’s marketing, events which need your attention, you need to make some time to talk to potential users and customers. And then of course, you’ll need to get your product live - who’ll take care of deployment?
This article contains a brief version of the stuff I usually tell people, who in one way or another ask for the best way to get their web app online, so it won’t take up the complete attention of one of the few team members, and slow everything down.
If you are working on a startup, have received a sizable amount of cloud credits from AWS and are getting ready to deploy your first product (a web application), this article is for you.
You probably already started learning about all the services in the AWS console, have read up on Docker for some reason, and now are wondering what of those to actually use. Should you use Amazon EC2 Container Service (ECS)? What about Elastic Load Balancing (ELBs), Auto Scaling groups (ASGs) and using S3 for static files? Feeling like you’re drowning in a sea of tools and services, while there’s so much more important stuff to do? Yep, but there’s good news.
You Don’t Need It
I know you have heard about Kubernetes being neat, everybody seems to be deploying using Docker containers and are able to scale up to fantastic amounts of requests per minute without breaking a sweat. You don’t need all of this for now - no, you really don’t.
What’s really important for you team, is being able to ship something people really value, without getting lost in stuff which will not improve your product. Building a big deployment pipeline and using all of those tools like the cool kids, takes a lot of time and usually comes with a big learning curve. That’s time you don’t want to invest for now - at this point you won’t get much return on it for the company.
You also don’t need immutable infrastructure - Cloud Formation et al. are amazing, but only if you’re in a position where they solve a pain you’re ready to have. You are far away from having those problems. The deployment times are not worth it for now. Neither should you invest into significant automation until it starts hurting - usually when you have 3 people working on the project.
A Monolith Server, Without Containers
So here’s what you need to get started: a single AWS EC2 instance. Pick a medium one, and you’re covered for a while. Choose Ubuntu 16.04, and run with it for now. But be sure to set up an Elastic IP for it - so you have a fixed public IP between restarts of the instance.
I’d suggest using a local PostgreSQL installation on the same machine, same for Redis of RabbitMQ if you need them. Log to local files on the disk - be sure to make use of the logs while you’re at it so there’s a point to them. Use Nginx to serve static files, and as a reverse proxy to your app. Have supervisord take care of keeping your services running. If you want to have your pages on HTTPS (using SSL/TLS certificates), take a look at Let’s Encrypt - running certbot on your monolith is really straightforward.
If you haven’t used Docker before, don’t jump into it without understanding what problems the tool solves, and how workflows look without it. Your learning curve will be smoother once you know the basics of deploying a web app. Save some time and effort.
Get comfortable with this setup, and start putting in work to kill pains which you REALLY encounter, when it’s time to fix them. Don’t use tools just because others are relying on them - you don’t know their reasoning, and their needs. At worst, it’s just a cargo-cult thing.
Don’t Get Fancy With Services, But You Can Use A Few
You probably wanted to have your code in repos anyway. Don’t self-host your own git (although gitolite is pretty nice). Bitbucket offers unlimited free private repos for your team. You can generate an ssh key on your monolith, enter it as a deploy key in your product repos. Once it’s time to deploy, SSH into your server, pull the repo and restart the application
Getting notified about errors in your web app is the least you can take care of. If you are not using Sentry yet, you really should start. As they provide a free plan, there’s no reason to rely on getting error-emails, or having your users send you 500-errors without being able to track them down.
Don’t self-host email, use G Suite. For automated stuff, you can take a look at Mailgun.
Keeping it simple, does not mean neglecting important tasks. The least you can do, is creating snapshots of your setup every once in a while, until you have time to implement a proper backup process. Creating a tar of all important files and folders daily, and saving it in two safe places (your laptop and S3) is an acceptable idea. You can shift to incremental backups eventually. Good enough will do for now.
Document Your Deployment Setup
Write down all the details, which you expect to have on the system, but are currently taking for granted. Just a section in your deployment README doc. What language runtime versions are you using? Which service versions do you expect to have, what libraries (with versions) did you need to install by hand? Thinking about this now, will help you fix stuff when it stops working, and to automate/improve your setup in the future.
What steps would you need to take, to setup your server as it is from scratch - starting with just a barebones Ubuntu installation? Take the configs you adjusted, and put them in an own ops repo for later reference.
This is a thing Docker is pretty neat for. You can write Dockerfiles, which bring up a local version of your dev/production environment. Those are a very good way to document your setup, as you’ll make sure you didn’t miss a necessary part or step. The setup would simply not work. But that’s something for later-you.
Document Your Deployment Steps
Of course you know how to deploy your code, but write it down anyway. Every step, like connecting to the server via ssh, pulling repos, what commands you use to keep submodules up to date, which directories you need to switch to and similar.
Once again, this will be very helpful when you start automating tedious tasks, or if your team grows. Also future-you is going to thank you instead of getting frustrating.
The same goes for regular maintenance tasks. Your process for backing up data, how you go about applying migrations to your data models. What to do if you deploy a new version which happens to be broken.
The First Stage, But Way to Go
With all of this in place, you’ll be all set to focus on your product. There’s a lot to do which would benefit you, but for now you will get more bang-for-the-buck by building a better product. Building amazing infrastructure around it can wait until it makes sense. Don’t worry about scaling too much. You’ll be able to handle it if your app is well architected.
Here are some steps you can take from this, to improve your setup and processes incrementally. Make sure to understand why you’re about to implement something, what benefit you expect and what pain it’s solving for you, your product and your team.
Low Hanging Fruits
Stop ssh-ing to your server. Start using automation - Fabric is a nice python module, making it easy to script remote operations. With it, you can write fab deploy and it will pull repos, restart services and even run migrations if you want it to.
Host your static files via S3. If you have a completely-static website, take a look at Netlify instead. You don’t need to do the busywork, and it’s got a free plan.
Start writing useful log messages and actually using them to understand your users. If you write stuff nobody ever looks at, you’re missing the point of having logs.
Ansible is a config management tool, which you will want to check out. It makes configuring servers from scratch a breeze, and makes it easy to keep multiple machines under control later on. You can use it to configure Vagrant development environments as well to get started. It feels good to know you can just type vagrant up, and have a virtual machine with your app running and ready to be worked on.
Monitoring is great. This way, you’ll be the first to know that your service is down. This is a bit more advanced - AWS offers some alerting functionality which should be completely sufficient. Otherwise, take a look at Prometheus. You can start using it on the same monolith, but should run it on another instance eventually.
Learn about Docker. Once you know how to deploy your app on a machine without containers, you will easily understand how it could be useful later on. If you want to start using it - start with development dependencies and your local dev environment. There be dragons in production.
Create a staging environment - it will look just like your production server, but be a safe place to try changes before pushing them live to everybody. Be sure to have enough users to make this worthwhile.
Continuous integration is a great way towards getting ready for having 3 and more people working on the same project. You’ll be able to run tests automatically and make sure no obviously-broken-code gets deployed on your servers. Before CI, make sure to have proper, useful tests in your app. Try to use a service for this, or setup a Jenkins container on your machine.