wget -qO- https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt-get update
sudo apt-get install -y nodejssee the file conf/etc/apache2/sites-available/tax.example.com.conf for
example apache configuration.
cd tax/front/
sudo cp \
conf/etc/apache2/sites-available/tax.example.com.conf \
/etc/apache2/sites-available/tax.example.com.confedit /etc/apache2/sites-available/tax.example.com.conf. make sure
to set
SSLCertificateFile /path/to/your/fullchain.pem
and
SSLCertificateKeyFile /path/to/your/privkey.pem
then enable the site and make apache read the new configuration
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2ensite tax.example.com
sudo systemctl restart apache2if you want to test locally, you can temporarily modify your local
/etc/hosts to add lines to redirect tax.example.com to apache on
your machine
# testing tax.example.com locally before deploying
127.0.0.1 tax.example.com
::1 tax.example.com
cd tax/front
sudo npm install -y pm2 -g
npm install
npm run build
pm2 start server.js --name "tax-front"
# browse to tax.example.com to see if it works
# the output of this might tell you to do something different than the
# next command
pm2 startup systemd
# the output of the prior command might give a different command than
# the following command. if it does, use that instead.
sudo env \
PATH=$PATH:/home/user/.config/nvm/versions/node/v18.20.5/bin \
/usr/local/lib/node_modules/pm2/bin/pm2 \
startup systemd \
-u user \
--hp /home/user
# it might look like this on the server
sudo env PATH=$PATH:/usr/bin \
/usr/lib/node_modules/pm2/bin/pm2 startup systemd \
-u user \
--hp /home/usercd tax/front
git pull
npm install
npm run build
pm2 restart tax-frontpm2 list # List applications
pm2 restart tax-app # Restart application
pm2 logs # View logs
pm2 monit # Monitor CPU/Memorycould i turn this into a service file with systemd?
should i turn this into a service file with systemd?
is it possible to tell pm2 to start the service on system boot?
is it possible to have the user “do.each.tax” run this using pm2?
“do.each.tax” is the name of a system user i just made for running the back end.
You can definitely turn your Node.js application into a systemd service instead of using PM2, but PM2 already provides its own integration with systemd that you’ve partially configured in your deployment instructions.
To answer your questions:
- PM2 startup on system boot: You’re already on the right track
with the
pm2 startup systemdcommand. This creates a systemd service that starts PM2 on boot, which in turn starts your application. - Running as user “do.each.tax”: Yes, you can have PM2 run your application as a specific user. Modify your PM2 startup command to:
sudo env PATH=$PATH:/usr/bin \
/usr/lib/node_modules/pm2/bin/pm2 startup systemd \
-u do.each.tax \
--hp /home/do.each.taxThen, you’ll need to:
# Switch to the do.each.tax user
sudo su - do.each.tax
# Navigate to your application directory
cd /path/to/tax/front
# Start the application with PM2
pm2 start server.js --name "tax-front"
# Save the PM2 process list
pm2 saveThis will ensure that PM2 runs your application as the do.each.tax user when the system boots.
i already did some stuff with pm2. how do i make sure i’m only running this once with pm2? i used the user “ozzloy” to run it before. can i list what stuff pm2 is currently doing?
when i run
pm2 start server.js --name "tax-front"as the user do.each.tax, does that mean it will start on boot in the future?
To manage your PM2 instances effectively:
- List current PM2 processes:
pm2 listThis will show all applications managed by PM2 for the current user.
- To avoid running duplicate instances, first check if “tax-front”
is already running:
pm2 listIf it’s already running, you can stop it or delete it:
pm2 stop tax-front pm2 delete tax-front - Regarding running as “do.each.tax” vs “ozzloy”:
- Each user has their own PM2 daemon
- If you want to migrate from ozzloy to do.each.tax:
- Stop and delete the process running as ozzloy
- Start it as do.each.tax
- Configure startup for do.each.tax
- Starting on boot: Simply running
pm2 startdoes NOT configure autostart on boot. For that, you need:# As do.each.tax user pm2 start server.js --name "tax-front" pm2 save # Then as root sudo env PATH=$PATH:/usr/bin \ /usr/lib/node_modules/pm2/bin/pm2 startup systemd \ -u do.each.tax \ --hp /home/do.each.tax
The proper sequence to switch to do.each.tax would be:
- Login as ozzloy and
pm2 delete tax-front - Login as do.each.tax, start the app, save the PM2 config
- Configure systemd to start do.each.tax’s PM2 on boot
sudo env PATH=$PATH:/usr/bin \
/usr/lib/node_modules/pm2/bin/pm2 startup systemd \
-u do.each.tax \
--hp /home/do.each.taxThis command configures PM2 to start automatically at system boot through systemd. Here’s a breakdown:
sudo- Runs the command with root privileges, necessary for creating systemd services.env PATH=$PATH:/usr/bin- Sets the environment PATH variable to include/usr/bin(where Node.js and PM2 binaries are typically located)./usr/lib/node_modules/pm2/bin/pm2- The full path to the PM2 executable.startup systemd- Tells PM2 to generate a systemd startup script.-u do.each.tax- Specifies that PM2 should run as the user “do.each.tax”.--hp /home/do.each.tax- Sets the home path of the user, where PM2 configuration files will be stored.
This command generates and installs a systemd service file that will automatically start PM2 (and consequently any apps saved in PM2’s configuration) when the system boots, running as the specified user.
i do not want this to use the path for the user ozzloy, i want it to use the path for the user do.each.tax
[PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u do.each.tax –hp /var/www/tax.each.do do.each.tax@each:~/front$ sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u do.each.tax –hp /var/www/tax.each.do [sudo] password for do.each.tax: sudo: a password is required do.each.tax@each:~/front$ ^C do.each.tax@each:~/front$ exit logout ozzloy@each:~$ sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u do.each.tax –hp /var/www/tax.each.do [PM2] Init System found: systemd Platform systemd Template [Unit] Description=PM2 process manager Documentation=https://pm2.keymetrics.io/ After=network.target
[Service] Type=forking User=do.each.tax LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity Environment=PATH=/home/ozzloy/.pyenv/plugins/pyenv-virtualenv/shims:/home/ozzloy/.pyenv/shims:/home/ozzloy/.pyenv/bin:/home/ozzloy/.local/bin:/home/ozzloy/.pyenv/plugins/pyenv-virtualenv/shims:/home/ozzloy/.pyenv/bin:/home/ozzloy/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin Environment=PM2_HOME=/var/www/tax.each.do/.pm2 PIDFile=/var/www/tax.each.do/.pm2/pm2.pid Restart=on-failure
ExecStart=/usr/lib/node_modules/pm2/bin/pm2 resurrect ExecReload=/usr/lib/node_modules/pm2/bin/pm2 reload all ExecStop=/usr/lib/node_modules/pm2/bin/pm2 kill
[Install] WantedBy=multi-user.target
Target path /etc/systemd/system/pm2-do.each.tax.service Command list [ ‘systemctl enable pm2-do.each.tax’ ] [PM2] Writing init configuration in /etc/systemd/system/pm2-do.each.tax.service [PM2] Making script booting at startup… [PM2] [-] Executing: systemctl enable pm2-do.each.tax… Created symlink /etc/systemd/system/multi-user.target.wants/pm2-do.each.tax.service → /etc/systemd/system/pm2-do.each.tax.service. [PM2] [v] Command successfully executed.
---------------------------------------[PM2] Freeze a process list on reboot via: $ pm2 save[PM2] Remove init script via: $ pm2 unstartup systemd
In the provided output, the issue is that when you run the sudo env
PATH=$PATH:/usr/bin pm2 startup command as the user ozzloy, it’s
using ozzloy’s PATH environment variable in the systemd service file
configuration instead of do.each.tax’s PATH.
To fix this, you need to explicitly set the PATH for
do.each.tax. You can modify the service file directly:
- Edit the service file:
sudo nano /etc/systemd/system/pm2-do.each.tax.service- Change the
Environment=PATH=line to use a standard system path ordo.each.tax’s path instead ofozzloy’s path:
Environment=PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin
- After editing, reload systemd:
sudo systemctl daemon-reloadThis will ensure the service uses a system path rather than ozzloy’s
user-specific path.
this is the file /etc/sudoers.d/do-each-tax
do.each.tax ALL=NOPASSWD: /bin/systemctl restart tax-back do.each.tax ALL=NOPASSWD: /bin/systemctl restart tax-front
in order for the service file to work?
The /etc/sudoers.d/do-each-tax file you’ve shown is not necessary
for the PM2 systemd service to work properly. These sudoers entries
would only be needed if:
- You’re using scripts where the
do.each.taxuser explicitly needs to runsudo systemctl restart tax-backorsudo systemctl restart tax-frontwithout a password prompt. - You have separate systemd services named
tax-backandtax-front(different from your PM2 setup).
For your PM2 setup:
- The PM2 service itself runs as
do.each.taxuser through systemd - PM2 manages your Node.js applications directly
- The systemd service (
pm2-do.each.tax.service) doesn’t require sudo privileges once it’s set up
If you’re planning to use scripts where do.each.tax needs to restart
services via sudo, keep the file. Otherwise, it’s not required for the
PM2 startup configuration you’ve established.
To restart your applications, do.each.tax would use PM2 commands
directly without sudo:
pm2 restart tax-fronti want to create a systemd service that runs server.js as the user do.tax.each and forget about pm2
Yes, you can absolutely create a direct systemd service for your Node.js application without using PM2. This is a cleaner approach if you don’t need PM2’s additional features like process monitoring or clustering.
Here’s how to create a systemd service for server.js:
- Create a new systemd service file:
sudo nano /etc/systemd/system/tax-front.service- Add the following content:
[Unit] Description=Tax Front-end Node.js Application After=network.target [Service] Type=simple User=do.each.tax WorkingDirectory=/var/www/tax.each.do/front ExecStart=/usr/bin/node /var/www/tax.each.do/front/server.js Restart=on-failure # Optional: Environment variables if needed # Environment=NODE_ENV=production # Set appropriate node memory limits Environment=NODE_OPTIONS=--max-old-space-size=2048 [Install] WantedBy=multi-user.target
- Enable and start the service:
sudo systemctl enable tax-front
sudo systemctl start tax-front- Check the status:
sudo systemctl status tax-front- If you were using PM2 before, you can remove it from autostart:
sudo systemctl disable pm2-do.each.tax
sudo rm /etc/systemd/system/pm2-do.each.tax.service
sudo systemctl daemon-reloadThis approach is much more straightforward than using PM2 if you don’t need its advanced features. Your application will start automatically on boot and will restart if it crashes.
To check what’s using ports 8082 and 3000, you can use:
lsof -i :8082
lsof -i :3000To kill the processes:
# If you know the PID from the lsof output
kill <PID>
# Or more forcefully
kill -9 <PID>
# Or directly using the port
fuser -k 8082/tcp
fuser -k 3000/tcphere’s what i get for lsof:
ozzloy@each:~$ lsof -i :8082 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME gunicorn 3770287 ozzloy 5u IPv6 7397592 0t0 TCP *:8082 (LISTEN) gunicorn 3770311 ozzloy 5u IPv6 7397592 0t0 TCP *:8082 (LISTEN) ozzloy@each:~$ lsof -i :3000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node\x20/ 3772073 ozzloy 19u IPv6 7400658 0t0 TCP *:3000 (LISTEN)
You have two processes to kill:
- A gunicorn web server on port 8082 (parent process 3770287)
- A Node.js application on port 3000 (process 3772073)
You can kill them using:
# Kill the gunicorn parent process (this should also terminate the child)
kill 3770287
# Or forcefully if needed: kill -9 3770287
# Kill the Node.js process
kill 3772073
# Or forcefully if needed: kill -9 3772073Then verify they’re gone with lsof -i :8082 and lsof -i :3000
again.
here’s what that got me
ozzloy@each:~$ kill 3770287 ozzloy@each:~$ kill 3772073 ozzloy@each:~$ lsof -i :8082 ozzloy@each:~$ lsof -i :3000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node\x20/ 560320 ozzloy 19u IPv6 8983903 0t0 TCP *:3000 (LISTEN)
It looks like the gunicorn process was successfully killed, but a new Node.js process has started on port 3000. This may indicate that you have some service or auto-restart mechanism launching Node.js applications.
Try killing this new process with:
kill -9 560320If it keeps coming back, you might need to:
- Find what’s restarting it (systemd service, pm2, nodemon, etc.)
- Stop the parent service:
# If it's a systemd service sudo systemctl stop service-name # If it's pm2 pm2 stop all # or specific app name
Check running services in your user space that might be responsible for the auto-restart.
oh yeah, i have pm2 running the front
ozzloy@each:~$ pm2 list ┌────┬──────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │ ├────┼──────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 0 │ tax-front │ default │ 0.0.0 │ fork │ 560320 │ 71s │ 1 │ online │ 0% │ 51.8mb │ ozzloy │ disabled │ └────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘ ozzloy@each:~$
Perfect! Now you can stop the Node.js application using pm2:
pm2 stop tax-frontIf you want to completely remove it from pm2’s process list (so it won’t restart when pm2 restarts):
pm2 delete tax-frontThis should properly stop the application running on port 3000. Verify
it’s no longer running with lsof -i :3000 afterward.