This website was built using Nuxt paired with Nuxt Content for dynamic content (such as this post) and is currently hosted at my personal VPS on Digital Ocean.
These days platforms such as Vercel or Netlify are a much better and easier solution for hosting static content, they come with automatic CI/CD for example, but where's the fun in that, right?
My current method for publishing updates and new posts are literally these steps:
pnpm generate
rsync -avz .output/public/ $1@$2:<remote_path>
Quite rudimentary as you can see, but it works well enough. Change something, build locally and push with SSH.
So why add complexity for what should be easy and simple? For that I got a couple of answers:
As mentioned above I'm familiar with building CD pipelines for back-end applications, but static content is new for me. A quick and dirty prompt to ChatGPT got me this workflow for Github Actions:
name: Deploy to Digital Ocean Droplet
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: "18.x"
- name: Install dependencies
run: npm install
- name: Build static files
run: npm run generate
- name: Copy files to the server
uses: appleboy/[email protected]
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "./dist/*"
target: "/path/to/your/deployed/files"
- name: Restart the web server (if needed)
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
sudo systemctl restart nginx
From the looks it looks simple enough, but I already see some points I'll have to touch, for example:
I'm writing this post as I setup this pipeline, so off to do some testing...
...some minutes later...
It did not work, of course, it never does. The first workflow I tried after some adjustments that I mentioned above:
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: "22.x"
- name: Install dependencies
run: pnpm install
- name: Build static files
run: pnpm generate
- name: Copy files to the server
uses: appleboy/[email protected]
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "./.output/*"
target: "<remote_path>"
What went wrong:
/home/runner/work/_temp/fa719c57-ba34-4847-a12f-7f670af314be.sh: line 1: pnpm: command not found
Off to do some research and try again...
...some minutes later...
It lives! Not on second try (never does), but it's working and this very post was published automagically. Way easier than I thought, took less than 40 minutes.
The final workflow:
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: "22.x"
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build static files
run: pnpm generate
- name: Copy files to the server
uses: appleboy/[email protected]
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
port: ${{ secrets.REMOTE_PORT }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "./.output/**"
target: "<remote_path>"