Set up 2025 (nick)

Using Ubuntu 24.04 LTS which already has python3.12

1. install tree

sudo apt-get install tree sudo apt install python3.12-venv

2. Install Oh My Zsh

sudo apt install zsh -y
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

modify .zshrc

# Nick added
alias python=python3.12

echo ".zshrc loaded ✅"

3. node

  1. Download with curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

  2. nvm install 24

4. Install pm2

npm install -g pm2

5. Install the404-API and GitHub

  1. from terminal: ssh-keygen -t ed25519 -C "nrodrig1@gmail.com"
  2. start ssh-agent: eval "$(ssh-agent -s)"
  3. create or modify ~/.ssh/config file
Host github.com
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
  1. place your ssh key in the Github page: https://github.com/settings/keys

6. Create folder structure

mkdir applications
mkdir databases
mkdir environments
mkdir project_resources
mkdir _config_files

7. Clone the404-API

git clone git@github.com:costa-rica/The404-API.git

Set up 2025 (OBE - shared)

1. copy datastore/NWS-Avatar03 or start new using the Ubuntu 24.04 LTS image

2. use /home/shared/

Make home/ shared with all development users

sudo groupadd developers
sudo usermod -aG developers nick
sudo chown -R root:developers /home/shared
sudo chmod -R 775 /home/shared

3. install tree

sudo apt-get install tree

4. Install Oh My Zsh

sudo apt install zsh -y
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

5. Update .zshrc

prepare for node.js

export NVM_DIR="/home/shared/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

# For pm2 to work
export PM2_HOME="/home/shared/.pm2"


# Set NPM_CONFIG_PREFIX only if nvm isn't already loaded
if [ -z "$NVM_DIR" ]; then
    export NPM_CONFIG_PREFIX="/home/shared/.npm"
    export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
fi

alias python=python3.12

echo ".zshrc loaded ✅"

6. node

  1. create /home/shared/.nvm directory
  2. Download with curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Download and install nvm:
nvm install 24
#corepack enable yarn

7. Install the404-API and GitHub

  1. from terminal: ssh-keygen -t ed25519 -C "nrodrig1@gmail.com"
  2. start ssh-agent: eval "$(ssh-agent -s)"
  3. create or modify ~/.ssh/config file
Host github.com
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
  1. place your ssh key in the Github page: https://github.com/settings/keys
  1. git clone git@github.com:costa-rica/The404-API.git

8. Install pm2

npm install -g pm2

OBE: Set up 2025 (prior to November 2025)

1. use /home/shared/

Make home/ shared with all development users

sudo groupadd developers
sudo usermod -aG developers nick
sudo chown -R root:developers /home/shared
sudo chmod -R 775 /home/shared

2. Install Oh My Zsh

sudo apt install zsh -y
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

install tree

sudo apt-get install tree

3. Update .zshrc

prepare for node.js

export NVM_DIR="/home/shared/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

# For pm2 to work
export PM2_HOME="/home/shared/.pm2"
## No more yarn
# export PATH="/home/shared/.yarn/bin:$PATH"
# export PATH="/home/shared/.config/yarn/global/node_modules/.bin:$PATH"

# Set NPM_CONFIG_PREFIX only if nvm isn't already loaded
if [ -z "$NVM_DIR" ]; then
    export NPM_CONFIG_PREFIX="/home/shared/.npm"
    export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
fi

echo ".zshrc loaded ✅"

4. node

  1. create /home/shared/.nvm directory
  2. Download with curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Download and install nvm:
nvm install 24
#corepack enable yarn

Assign privileges

sudo chown -R :developers /home/shared/.nvm
sudo chmod -R 775 /home/shared/.nvm # This one might not be necessary ?

5. Install the404Back and GitHub

  1. from terminal: ssh-keygen -t ed25519 -C "nrodrig1@gmail.com"
  2. start ssh-agent: eval "$(ssh-agent -s)"
  3. create or modify ~/.ssh/config file
Host github.com
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
  1. place your ssh key in the Github page: https://github.com/settings/keys
  1. git clone git@github.com:costa-rica/The404-API.git

6. Install pm2

npm install -g pm2

Implement logrotate

7. move all the .yarn .yarnrc .nvm, .pm2, etc. to /home/shared/

mv /home/nick/.yarn /home/shared/ mv /home/nick/.yarnrc /home/shared/ mv /home/nick/.nvm /home/shared/ mv /home/nick/.pm2 /home/shared/

8. nginx

sudo apt install nginx -y

sudo apt install ufw -y
sudo ufw allow ssh
sudo ufw allow "Nginx full"
sudo ufw allow from 192.168.100.166 to any port 8000
sudo ufw allow 8000
sudo ufw allow from 192.168.1.134 # allows to all ports on this machine
sudo ufw enable

Troubleshooting

Set up 2024 and prior

This is used setup machines that deploy web applications on Ubuntu20.04.4 LTS focal. The current process just focuses on Python Flask websites.

Update and install Python

sudo apt update && sudo apt upgrade -y
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt-get install python3.11-full -y
#See all python installed in machine
ls /usr/bin/python*

Install Oh My Zsh

sudo apt install zsh -y
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

if no option to change default shell user:

chsh -s $(which zsh)

.profile or .zshrc files

alias python=python3.11

install nginx and firewalls

sudo apt install nginx -y

sudo apt install ufw -y
sudo ufw allow ssh
sudo ufw allow "Nginx full"
sudo ufw allow from 192.168.100.166 to any port 8000
sudo ufw allow 8000
sudo ufw enable

https certify

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d what-sticks.com -d www.what-sticks.com -d what-sticks-health.com -d www.what-sticks-health.com -d api.what-sticks.com -d api.what-sticks-health.com

sudo certbot --nginx -d venturer.dashanddata.com -d dev.what-sticks.com -d dev.api10.what-sticks.com -d pioneer02.dashanddata.com

sudo certbot --nginx -d nhtsa-dash.kineticmetrics.com -d demo.kmdashboard.dashboardsanddatabases.com

# certbot auto renewal
sudo systemctl status certbot.timer

# check certbo auto renewal
sudo certbot renew --dry-run

# starts service on boot
sudo systemctl enable ws08web

number of workers

1- get number of cores

nproc --all

2- calc number of workers based on gunicorn documenetation (CoreyS): 2 x num_cores

change computer (host name)

sudo hostnamectl set-hostname newNameHere
## edit this file with all the old names:
sudo nano /etc/hosts
sudo reboot
## This worked even in a WMWare VM

sudo unable to resolve host name or service not known

su - root
cat /etc/hosts
nano /etc/hosts

Multipathd warnings on server sys.log file

sudo systemctl stop multipathd
sudo systemctl disable multipathd

Install specific version of Python

Install prerequisites:

sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev wget

download source code

wget https://www.python.org/ftp/python/3.11.7/Python-3.11.7.tgz

extract:

tar -xf Python-3.11.7.tgz

configure:

cd Python-3.11.7
./configure --enable-optimizations

### Build Python from source and install it
make -j$(nproc)
### make -j$(nproc) <--- takes 20 minutes
sudo make altinstall
### Using make altinstall instead of make install prevents the new Python version from

### check it worked:
python3.11 --version

### After installation you can delete the Python-3.11.7  Python-3.11.7.tgz files

VMWare

Reverser Proxy Server:

VM Options > Configuration Parameters to add:

Change password

  1. terminal passwd

To allow short and simple password must edit file

  1. terminal: sudo nano /etc/pam.d/common-password
  2. To allow for short and simple password replace line password [success=1 default=ignore] pam_unix.so obscure sha512 to password [success=1 default=ignore] pam_unix.so sha512 minlen=4

install latest version of node

Method from nodejs.org

# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# Download and install Node.js:
nvm install 22

# Verify the Node.js version:
node -v # Should print "v22.12.0".
nvm current # Should print "v22.12.0".

# Download and install Yarn:
corepack enable yarn

# Verify Yarn version:
yarn -v

.zshrc file example with Node.js:

# 🛠️ Ensure Language Environment
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

# 🛠️ Node.js & NVM Setup
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # Load nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # Load nvm bash_completion

# 🛠️ Ensure Yarn Global Packages Are in PATH
export PATH="$(yarn global bin):$PATH"

# 🛠️ Source .profile (if it exists) for additional settings
if [ -f ~/.profile ]; then
  source ~/.profile
fi

# ✅ Debugging Message (Optional)
echo ".zshrc loaded successfully ✅ "

pm2

pm2 cron_restart

➜  ~ pm2 stop 16
[PM2] Applying action stopProcessId on app [16](ids: [ '16' ])
[PM2] [NewsNexusGNewRequester](16) ✓
[PM2][WARN] App NewsNexusGNewRequester stopped but CRON RESTART is still UP 42 6 * * *

pm2 Commands

install pm2

install global

yarn global add pm2 or npm install -g pm2

install through yarn

  1. yarn global add pm2
    • check that pm2 --version works otherwise edit PATH from .bashrc
    1. get binary path by yarn global bin
    2. in the .bashrc file (via nano ~/.bashrc) add export PATH="$PATH:/home/your-username/.yarn/bin"
    3. source ~/.bashrc
    4. check pm2 version

run app using pm2

  1. pm2 start app.js or pm2 start server.js
  2. give it a name pm2 start server.js --name ExpressApi01

pm2 ecosystem.config.js file that works

module.exports = {
  apps: [
    {
      name: "404Manager",
      script: "server.js",
      cwd: "/home/dashanddata_user/applications/ServerManagerBackend/",
      watch: true,
      env: {
        NODE_ENV: "production",
        API_KEY: "node-app-api-key",
        PORT: 8000, // Add the port number here
      },
    },
    {
      name: "DevelopmentWebApp",
      script: "/home/dashanddata_user/environments/my_venv/bin/gunicorn",
      args: "-w 3 -b 0.0.0.0:8001 --timeout 600 run:app",
      cwd: "/home/dashanddata_user/applications/DevelopmentWebApp", // Working directory
      interpreter: "none", // Prevents pm2 from using Node.js for this script
      env: {
        FLASK_CONFIG_TYPE: "dev",
        PATH: "/home/dashanddata_user/environments/my_venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
      },
    },
    {
      name: "DevelopmentWebApp02", // Name of your app
      script: "/home/dashanddata_user/environments/my_venv/bin/gunicorn",
      args: "-w 3 --timeout 600 run:app",
      cwd: "/home/dashanddata_user/applications/DevelopmentWebApp02", // Working directory
      interpreter: "none", // Prevents pm2 from using Node.js for this script
      env: {
        FLASK_CONFIG_TYPE: "dev",
        PATH: "/home/dashanddata_user/environments/my_venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
        PORT: 8002,
      },
    },
    // Add more apps here as needed
  ],
};

pm2 start on boot, reboot, or whenever the server is off and back on again

  1. 'pm2 startup`
  2. pm2 will display a line of code to copy and paste like this one:
sudo env PATH=$PATH:/usr/bin /home/dashanddata_user/.config/yarn/global/node_modules/pm2/bin/pm2 startup systemd -u dashanddata_user --hp /home/dashanddata_user
sudo env \
  PM2_HOME=/home/shared/.pm2 \
  PATH=/home/shared/.nvm/versions/node/v22.14.0/bin:$PATH \
  /home/shared/.nvm/versions/node/v22.14.0/lib/node_modules/pm2/bin/pm2 \
  startup systemd -u nick --hp /home/shared
  1. 'pm2 save`

additional steps for the /home/shared/ case (2025-08-16)

  1. Update the service to point at the shared PM2 home: from terminal do sudo systemctl edit pm2-nick and enter:
[Service]
Environment=PM2_HOME=/home/shared/.pm2
Environment=PATH=%h/.nvm/versions/node/v22.14.0/bin:/home/shared/.nvm/versions/node/v22.14.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
  1. Reload the service: sudo systemctl daemon-reload
  2. Might need the following as well: sudo systemctl enable pm2-nick sudo systemctl restart pm2-nick

undo pm2 start on boot

pm2 unstartup systemd

Run Express App on server

  1. move files to server
  2. install yarn
  3. use nginx .conf
    • if nginx sudo nginx -t shows a problem search for duplicates of directed route by using: grep -r "server_name expressapi01.dashanddata.com" /etc/nginx/sites-available/ or some variation. If this is the case probably need to replace the default file in the /etc/nginx/sites-available/ directory.
  4. open ports that app is running on

Run Express App on behind reverse proxy server

  1. create nginx file for reverse proxy machine
server {
    listen 80;
    server_name [your_domain.com];
    client_max_body_size 1G;

    location / {
        proxy_pass http://192.168.1.18:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 600s;  # Sets the timeout to 600 seconds
    }

    location /static {
        proxy_pass http://192.168.1.18:8000/static;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 600s;  # Sets the timeout to 600 seconds
    }
}
  1. move file to /etc/nginx/sites-available
  2. create link to /etc/nginx/sites-enabled/ using: sudo ln -s /etc/nginx/sites-available/[your_domain.com] /etc/nginx/sites-enabled/
  3. check nginx syntax and files are ok sudo nginx -t
  4. reload nginx sudo systemctl reload nginx
  5. open ufw port to server sudo ufw allow from [reverse_proxy_server_local_ip] to any port [port_app_is_deployed_on]

Trouble shooting

Node install issue

➜  The404Back git:(main) pm2 status
zsh: command not found: pm2
  1. add to .zshrc
# Nick added:

export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

# Source .profile if it exists
if [ -f ~/.profile ]; then
  source ~/.profile
fi
# Load NVM if it exists
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion"
echo ".zshrc loaded"
  1. add to .profile
export PATH="$PATH:/home/dashanddata_user/.yarn/bin"
echo ".profile loaded"

Terminal warning: "manpath: can't set the locale; make sure $LC_* and $LANG are correct"

Node for all Ubuntu users

  1. make the shared node follder
sudo mkdir -p /usr/local/nvm
sudo cp -r ~/.nvm/* /usr/local/nvm/
  1. install node (or copy node from /home/nick/) to /etc/profile.d/nvm.sh

  2. add to to the nvm.sh file sudo nano /etc/profile.d/nvm.sh

  3. add

export NVM_DIR="/usr/local/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # Load nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # Enable nvm auto-completion
  1. any user can then source /etc/profile.d/nvm.sh to activate the nvm.

Make files that were created by individual users accessible to all

  1. create a group: sudo groupadd developers
  2. add users to group: sudo usermod -aG developers <username> - do this for each user
  3. Change the group ownership of all folders: sudo chown -R :developers /home/applications/
  4. To allow all users in the group to create, delete, and modify files: sudo chmod -R 775 /home/applications/
  5. users might need to exit and re-enter the terminal

Create Jump User

  1. create jumpuser sudo adduser jumpuser on both machine01 (reverse proxy) and kv09 (destintion)
  2. add rule in machine01's sshd_config file (/etc/ssh/sshd_config) with the correct instructions you must sudo systemctl restart ssh
Match User jumpuser
        ForceCommand ssh jumpuser@192.168.1.72
        PermitTTY yes
        AllowTcpForwarding yes
  1. jumpuser's workstation they need to create a ssh-keygen file like: ssh-keygen -t ed25519 -C "jumpuser_to_machine01"
  2. jumpuser's workstation they need to copy the public key to machine01 like: ssh-copy-id jumpuser@82.66.246.192 where the ip address is the public ip that will get routed to the port 22 of the reverse proxy server.
  3. jumpuser should be able to ssh jumpuser@82.66.246.192

OBE Jumpuser

  1. create jumpuser in Machine01: sudo adduser jumpuser

  2. Make public key from user's computer (.i.e the computer that will want to access DeveloperServer through Machine01) ssh-keygen -t rsa -b 4096

  3. Add Developer Public Keys to jumpuser

  1. from Machine01 edit sshd_config

    1. from terminal (Machine01) enter: sudo nano /etc/ssh/sshd_config
    2. modify file by adding to the bottom:
      Match User jumpuser
              ForceCommand ssh dashanddata@192.168.1.136
              PermitTTY yes
              AllowTcpForwarding yes
    
    1. restart ssh: sudo systemctl restart ssh

Helpful resourece: https://www.youtube.com/watch?v=KIeBC7NIzj4

Other stuff that seemed to kind of work Tunneling one terminal:

  1. both work locally to access machine01 ssh -L 2222:192.168.1.134:22 jumpuser@82.66.246.192 ssh -L 0.0.0.0:2222:192.168.1.136:22 jumpuser@82.66.246.192

but this does not work to access Machine02 from Machine01 ssh dashanddata_user@localhost -p 2222 ssh dashanddata_user@192.168.1.134 -p 2222

END OBE Jumpuser

VMWare Cloning VM

Step 1: copy vmdk and vmx files to new folder with new VM name

Step 2: ☝️ [After copy is finished] turn on old VM

Step 3: register the cloned VM

Step 4 (Ubuntu 24.04 LTS): update IP address

Regenerate the machine ID (this is the key part)

sudo rm -f /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo systemd-machine-id-setup
sudo systemctl restart systemd-networkd
sudo reboot

Step 4 (Ubuntu 20.04 LTS): apply new static IP address

network:
  ethernets:
    ens160:
      dhcp4: false
      addresses:
        - 192.168.100.<unused_ip>/24
      gateway4: 192.168.100.1
      nameservers:
        addresses:
          - 8.8.8.8
  version: 2

Step 5: shutdown and restart server

Running Python Flask App on Ubuntu / PM2

the pm2 ecosystem.config.js file

    {
      name: "Samurai02APIRag",
      script: "/home/shared/applications/Samurai02APIRag/run.py",
      interpreter: "/home/shared/environments/samurai02/bin/python",
      cwd: "/home/shared/applications/Samurai02APIRag",
      env: {
        FLASK_CONFIG_TYPE: "prod",
      },
    },

OBE?

    {
      name: "Samurai02APIRag",
      script: "/home/shared/environments/samurai02/bin/gunicorn",
      args: "-w 3 -b 0.0.0.0:8003 --timeout 600 run:app",
      cwd: "/home/shared/applications/Samurai02APIRag",
      interpreter: "none", // direct python path from venv
      autorestart: true, // don't restart on crash; just run at scheduled time
      watch: false, // no need to watch files
      env: {
        FLASK_CONFIG_TYPE: "prod",
        PATH: "/home/shared/environments/samurai02/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
      },
    },

service file

[Unit]
Description=Gunicorn instance to serve Samurai02 API Production on Samurai02.
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/home/nick/applications/Samurai02APIRag
Environment="PATH=/home/nick/environments/samurai02/bin"
Environment="FLASK_CONFIG_TYPE=prod"
ExecStart=/home/nick/environments/samurai02/bin/gunicorn -w 3 -b 0.0.0.0:8003  --timeout 600 run:app

[Install]
WantedBy=multi-user.target

run.py

import os
from dotenv import load_dotenv
load_dotenv()
from app_package import create_app

app = create_app()

if __name__ == '__main__':
    port = int(os.environ.get("FLASK_RUN_PORT"))
    host = os.environ.get("FLASK_RUN_HOST")
    app.run(host=host, port=port)

set up .env

FLASK_RUN_HOST="0.0.0.0"
FLASK_RUN_PORT=8003

set up .flaskenv

the server has not been responding well to placiing variables in the .flaskenv file.

FLASK_APP=run
FLASK_DEBUG=1

Neosmay Server Device