I'm moving a lot of personal projects that are costing me money from AWS to the 508 project server, a VPS we have for hosting all our portfolio projects. We have our own VPS rather than using tools like Heroku or AWS because while such tools are great for scaling, lately minimum costs have been too high for small projects with even the slightest bit of complexity (e.g., a database). With our own VPS we can easily just spin up a bunch of docker containers for our more complex projects (such as the guessagame.508.dev backend), and, we can very easily deploy static sites using nginx (such as the guessagame.508.dev frontend).
In doing so I thought it'd be cool to get a script that I can just fire off every time I want to spin up a new project that does the myriad of steps necesssary. That turned out to be too time consuming to figure out all the weird little nitty gritty, but in the effort I was at least forced to finally write down all the steps from A-Z. The project where this lives has all the steps in the README, but for now they are as follows:
-
Ensure Domain $NAME.508.dev A record is created on our domain manager.
I want 508 projects to be easily shown off as simply as pulling out your phone and navigating to a URL. To that end we try to give all projects $NAME.508.dev domains. So first, we need to create an A record in our domain manager pointing at the VPS' IP address, which is static. If it wasn't static, we'd need to set up dynamic dns in our domain manager (first choosing one that has this feature), and then set up ddclient on our VPS.
-
Create new user account of name $NAME
Each project has its own user on the VPS, with its own home directory. Vaguely, this is a security measure, so if somehow one project is compromised, hopefully that doesn't result in ALL of our projects being compromised.
-
Create SSH keys
This step is the most likely to change over time. In short, we need to make SSH keys that github actions can use to run commands on the VPS. Generally you can make these on your local machine, save them somewhere, and then copy the public key to the new account's
authorized_keys
file. -
Add user to group
www-data
Static web-hsoted files are all kept in the
www-data
user's directory, under folders named by project name. Then, we just point nginx at each directory (see below). You need to be a member ofwww-data
to write to this directory. Technically this means one compromised project can wipe out any others that use this strategy. IDK what to do about this yet. I guess... I could just have nginx point to static directories in each project'shome
folder... I need to do that in a way that the nginx program can read all those different directories. TODO to think on that. -
Create directory for html, css, js output in
/home/www-data/
-
Create file of
/etc/nginx/sites-available/$NAME.508.dev
Juicy bit of nginx config, which in short listens to requests to
$NAME.508.dev
, and then handles them by serving static files in the givenroot
directory (see the nginx config below). It can also handle SPAs by sending 404s directly back toindex.html
, which allows an SPA's route manager to handle these 404s. I should actually remove that part of the config for static projects. TODO. -
Softlink into `/etc/nginx/sites-enabled/`
According to nginx norms, and my nginx config, the
sites-enabled
directory is where config files are actually picked up and made live. -
Temporarily take down nginx, Create https certificate, Bring up nginx again
Getting an HTTPs certificate requires the A record to be set up, and then letting certbot spin up a temporary webserver. This webserver needs to listen on port 80, which nginx is already listening on, so you gotta take it down for a second (I think). Then certbot does its thing, gets you an https ticket, and you spin nginx back up. Certbot command below.
That's just the first iteration, see the project repo for the most up to date methodology we use.
After that's done, a project needs to have github actions set up to auto deploy on push. Here's an example of how that works for this website.
Nginx config for a static site
server {
listen 80;
listen [::]:80;
server_name $NAME.508.dev www.$NAME.508.dev;
return 301 https://$server_name$request_uri;
}
server {
server_name $NAME.508.dev;
gzip on;
location / {
autoindex on;
root /home/www-data/$NAME/;
error_page 404 = /index.html;
}
listen 443 ssl;
listen [::]:443;
ssl_certificate /etc/letsencrypt/live/$NAME.508.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$NAME.508.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
}
Nginx config for a server or anything that's getting proxied to
server {
listen 80;
listen [::]:80;
server_name $NAME.508.dev www.$NAME.508.dev;
return 301 https://$server_name$request_uri;
}
server {
server_name $NAME.508.dev;
gzip on;
location / {
proxy_pass http://localhost:8000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
listen 443 ssl;
listen [::]:443;
ssl_certificate /etc/letsencrypt/live/$NAME.508.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$NAME.508.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
}
Certbot command
sudo certbot certonly --standalone -d $NAME.508.dev
Other command examples can be seen in the setup script repo