I've recently deployed my first application and I want to be able to update the website code roughly once or twice a week. To update my website, I push my code up to github, using git. I then clone the repository on my ubuntu server, which uses pm2 to keep the server connected. My website is using ec2 for hosting. Once the new code is cloned, I run 'npm install' on my app, add the new 'build' folder and update the .env variables. I then restart the pm2 server and that's it. This doesn't cause much downtime (maybe a few minutes) and it probably takes me like 20 minutes which is fine, but my question is... Am I doing this wrong? Is there a more efficient way to update my website? Maybe one without downtime or something that's much faster?
Yes, there are better ways to do it! What you're describing is generally known as Continuous Integration/Continuous Deployment, or CI/CD. CI/CD is an area of developer operations (devops) that includes tools and techniques used to automate the build, testing, and deployment of an application. You have several options, depending on how robust this solution needs to be. Here are a few:
You could use Github Actions to automate something very similar to what you do right now. Instead of running the commands manually, you would script them to run in a Docker container hosted on Github. You could configure the action to run when you tag a commit, or commit on a specific branch. This would remove the tedium of running the commands manually, but wouldn't eliminate downtime during the deployment process. Github's free plan includes 2,000 automation minutes per month, which sounds like it would be more than adequate for your needs. Of course it could be used to do far more than this simple scenario, too.
AWS CodePipeline is AWS's CI/CD service. Similar to GitHub actions, it can be used to orchestrate a complex sequence of actions to build, test, and deploy applications. AWS allows one active pipeline on its free tier. There's probably not a compelling difference between Github Actions and CodePipeline for your use case, so you may want to read the getting started documents and pick the one that appeals to you more.
Instead of using a single EC2 instance, you could create your application in an Elastic Beanstalk environment. Elastic Beanstalk is a service that simplifies deployment of common application patterns. EB includes load balancing, autoscaling, and managing deployments across multiple EC2 instances. This would enable you to deploy with no downtime, as the EB environment will manage the deployments and take instances out of service as they're being updated, while leaving the other instances in service. Using EB would probably incur additional cost over what you're doing now, and you may find it limiting for use cases that diverge from the application patterns it supports. But for a simple nodejs app deployed on a couple of web servers, this would work just fine.
There are lots of other options, but I think one of the above would serve as an excellent introduction to CI/CD in AWS.