Deploying to NUCs
Setting Github
To ensure secure access to your GitHub repository from a NUC, generate an SSH key pair and add the public key to your GitHub account.
ssh-keygen -t ed25519 -C "your_email@example.com"
Copy the contents of the public key by printing it
cat ~/.ssh/id_ed25519.pub
Please add the above public SSH key to your GitHub account by following these steps:
- Go to https://github.com/settings/keys
- Click the 'New SSH key' button.
- Enter a title for the key and paste the copied public key into the 'Key' field.
- Click the 'Add SSH key' button to save the key.
Optionally you can test the connection with GitHub by running:
ssh -T git@github.com
Installing Node
We prefer using NVM to manage Node versions. To install NVM, run the following command:
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
Update your system's environment: You can source the file with the following command:
source ~/.bashrc
Install Node
nvm install --lts
Optional: Installing git LFS
Sometimes activations have large videos and assets that need to be stored in the repository. To install git LFS, run the following command:
sudo apt-get install git-lfs
Then run the following command to initialize git LFS:
git lfs install
Installing Postgres
Install PostgreSQL
sudo apt-get install -y postgresql postgresql-contrib
Start the PostgreSQL service
sudo systemctl start postgresql
Enable PostgreSQL to start on boot
sudo systemctl enable postgresql
Set the password
This will get you into the PostgreSQL command line interface.
sudo -u postgres psql
Follow the prompt to set a password for the 'postgres' user.
\password postgres
Quit the PostgreSQL CLI.
\q
You can now add the .env
var
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/activation?schema=public
Adding PM2 scripts
Install pm2 globally
npm install pm2 -g
Run the pm2 command to initiate a config file
pm2 init
Then change the contents of the generated file ecosystem.config.js
.
module.exports = {
apps: [
{
name: "activation-app",
script: "./node_modules/next/dist/bin/next", // changes if using sockets
args: "start", // changes if using sockets
instances: 2,
exec_mode: "cluster",
wait_ready: true,
log_date_format: "DD-MM HH:mm:ss.SSS",
listen_timeout: 3000,
env: {
NODE_ENV: "production",
},
},
],
};
Add the following scripts to the package.json
file:
"pm2:start": "pm2 start",
"pm2:restart": "pm2 reload all --update-env",
"pm2:kill": "pm2 delete all",
"pm2:deploy": "./deploy-nuc.sh",
Automatic start on boot
To ensure that the app starts automatically on boot, run after starting the app: Ref Link: https://pm2.keymetrics.io/docs/usage/startup/
# you need to run the app before saving
npm run pm2:start
# To automatically generate and configuration a startup script just type the command (without sudo)
pm2 startup
# save the current pm2 list
pm2 save
logging on the nucs
To view the logs on the nucs, run the following command:
pm2 logs activation-app
Near Zero Downtime deployments with pm2
You can use this simple strategy, although it won't guarantee "zero" downtime, but it would be much better than the current experience. Basically, you build your project into a temp folder, and then delete existing .next folder, and rename your temp folder as .next.
Add this to your next.config.js file:
const nextConfig = {
distDir: process.env.OUTPUT_DIR || ".next",
};
Create a script with the following contents: ie: ./deploy-nuc.sh
#!/bin/bash
echo "Deployment starting..."
# pull latest changes
git pull origin master
# store latest hash to include in build
# you can use this to track if the latest report, for example you can build a Next.js
# API route to return this
LATEST_COMMIT_HASH=$(git rev-parse --short HEAD)
export LATEST_COMMIT_HASH
# install dependencies if any
npm ci || exit
# set build folder to `temp` and build
OUTPUT_DIR=temp npm run build || exit
if [ ! -d "temp" ]; then
echo '\033[31m temp directory does not exist!\033[0m'
exit 1;
fi
# delete `.next` folder
rm -rf .next
# rename `temp` folder to `.next`
mv temp .next
# run next start
if pm2 describe activation-app > /dev/null 2>&1; then
echo "activation-app is running, restarting..."
npm run pm2:restart
else
echo "activation-app is not running, starting..."
npm run pm2:start
fi
echo "Deployment done."
Make the file executable
chmod +x ./deploy-nuc.sh
Automatic updates from Github
Set up a workflow which GitHub will prompt you to do. You can start with a simple workflow file like this in .github/workflows/deploy.yml
.
name: Deploy to NUCS
on:
workflow_dispatch:
# Uncomment if you want to autodeploy
# push:
# branches:
# - master
jobs:
check-build:
name: Check Build
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
# Uncomment if needed. Adjust to your project needs
# Creates dotfiles using GitHub's secrets
# https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions
# - name: Create dotfiles
# run: |
# echo "${{ secrets.NPMRC }}" > .npmrc
# echo "${{ secrets.ENV_FILE }}" > .env
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
deploy:
name: Deploy to NUC
runs-on: ubuntu-latest
needs: check-build
strategy:
matrix:
nuc: [NUC1, NUC2] # add as many as needed [NUC1, NUC2, ...]
steps:
- uses: actions/checkout@v4
- name: Parse NUC credentials
id: parse_creds
run: |
IFS=':' read -r HOST USERNAME PASSWORD PORT <<< "${{ secrets[format('NUC_CREDENTIALS_{0}', matrix.nuc)] }}"
echo "::add-mask::$HOST" # Mask the host to avoid it showing in logs
echo "::add-mask::$USERNAME" # Mask the username to avoid it showing in logs
echo "::add-mask::$PASSWORD" # Mask the password to avoid it showing in logs
echo "HOST=$HOST" >> $GITHUB_ENV
echo "USERNAME=$USERNAME" >> $GITHUB_ENV
echo "PASSWORD=$PASSWORD" >> $GITHUB_ENV
echo "PORT=$PORT" >> $GITHUB_ENV
- name: Deploy to server
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.HOST }}
username: ${{ env.USERNAME }}
password: ${{ env.PASSWORD }}
port: ${{ env.PORT }}
script: |
cd code/
export NVM_DIR=~/.nvm # for some reason nvm is not ready
source ~/.nvm/nvm.sh
./deploy-nuc.sh
Create the appropriate secrets in your repository settings. with the following format
NUC_CREDENTIALS_NUC1=host:username:password:port
NUC_CREDENTIALS_NUC2=102.168.3.22:wondersauce:******:22