Ravindra BagaleCourses & study guides मराठी Track your progress

Host a Node.js Express App on AWS EC2 with PM2 and Nginx

Node.js runs JavaScript on the server, and Express is the small framework most Node.js APIs are built with. If you have hosted a Python app, the road is almost the same; only the tools change names. In this chapter you compare the two side by side, install Node.js and npm on an Amazon Linux 2023 EC2 instance, write package.json and an Express index.js with a JSON route, install the libraries into node_modules, test the app on port 3000, keep it running with PM2 (also after a reboot), and put nginx in front so visitors use plain port 80.

What you'll learn in this class

  • Hosting a Python app vs a Node.js app, step by step side by side
  • Installing Node.js and npm on Amazon Linux 2023 and on Ubuntu
  • package.json: name, version, dependencies and devDependencies
  • An Express index.js with a / route and a /getusers JSON route
  • npm install, the node_modules folder and package-lock.json
  • Testing with node index.js and a temporary port 3000 rule
  • PM2: start, list, logs, restart, stop, and pm2 startup plus pm2 save for reboots
  • nginx proxy_pass to port 3000, sudo nginx -t, and closing port 3000
  • Fixing the usual errors: Cannot find module, EADDRINUSE, 502 Bad Gateway

1. Python app vs Node.js app, side by side

Why: a dynamic app, PHP, Python or Node.js, is hosted the same way: install the language, install the app's libraries, run the app on an internal port, keep it running in the background, and put nginx in front on port 80. Once you see the steps side by side, a new language is only new command names.

Hosting a Python app vs a Node.js app Install Python sudo yum install python3-pip -y Node.js sudo yum install nodejs24 -y App folder Python mkdir pythonapp Node.js mkdir nodeapp Library list Python requirements.txt Node.js package.json Framework Python Flask / Django Node.js Express Code Python app.py Node.js index.js Install libraries Python pip install -r requirements.txt Node.js npm install Test run Python python3 app.py :5000 Node.js node index.js :3000 Background Python gunicorn Node.js pm2 start index.js In front Python nginx proxy_pass :5000 Node.js nginx proxy_pass :3000 Python libraries in a virtual environment python3 -m venvmyenv Node.js no venv: libraries in the app's own node_modules/

Figure 1. The same nine steps for a Python app and a Node.js app: install, folder, library list, framework, code, install libraries, test run, background run and nginx in front. The big difference: Python needs a virtual environment, Node.js keeps each app's libraries in its own node_modules folder.

Classroom line

Python needs a virtual environment; Node.js doesn't.

The Python side is explained step by step in Host a Python Flask app on EC2 with gunicorn and nginx.

2. Install Node.js and npm

Why: Without Node.js the server cannot run JavaScript at all, and npm is how you fetch Express. Picking a current version means you keep getting security fixes.

What: Node.js runs JavaScript outside the browser. npm (Node Package Manager) downloads libraries such as Express; it comes with Node.js.

How, on Amazon Linux 2023:

  1. Launch an Amazon Linux 2023 instance (t2.micro or t3.micro) with a security group that allows SSH (22) from My IP and HTTP (80) from anywhere, and connect with ssh -i mykey.pem ec2-user@PUBLIC_IP.
  2. Install Node.js and check the versions:

    sudo yum install nodejs24 -y
    node -v
    npm -v
    
  3. node -v prints v24.x and npm -v prints the npm version. The nodejs24 package installs npm with it.

  4. Amazon Linux 2023 has one package per Node.js version: nodejs22 and nodejs24 are current. The plain nodejs package is Node.js 18, which no longer gets security fixes, so don't use it for a new app.

How, on Ubuntu: the package is also called nodejs, and npm is a separate package:

sudo apt update
sudo apt install nodejs npm -y
node -v

3. Create the project and package.json

Why: package.json is what lets any server rebuild the app with one npm install. Without it you would have to remember every library by hand.

What: package.json describes the app: its name, its version, and the libraries it needs. It plays the role of requirements.txt, written as JSON: "key": "value" pairs inside { }, separated by commas.

How:

  1. Create the folder and open the file:

    mkdir nodeapp
    cd nodeapp
    nano package.json
    
  2. Write:

    {
      "name": "node-app",
      "description": "sample Express API",
      "version": "0.0.1",
      "private": true,
      "dependencies": {
        "express": "^5.3.0"
      },
      "devDependencies": {
        "mocha": "^12.0.3",
        "supertest": "^7.3.1"
      }
    }
    
  3. Save with Ctrl+O, Enter, and leave with Ctrl+X.

What each part means:

  1. name and version identify the app. "private": true stops it from being published to the npm registry by mistake.
  2. dependencies are the libraries the app needs to run: here only Express.
  3. devDependencies are only needed while you write and test the code: mocha runs tests and supertest sends test requests to the app. The live app never uses them.
  4. ^5.3.0 means "version 5.3.0 or any newer 5.x". npm never jumps to 6.0 by itself, because a new major version can break the code.

Classroom line

Libraries needed only for coding and testing are called development dependencies.

package.json: two kinds of libraries{ "name": "node-app", "version": "0.0.1", "dependencies": { "express": "^5.3.0" }, "devDependencies": { "mocha": "^12.0.3", "supertest": "^7.3.1" }}dependenciesneeded to RUN the app, on every serverexpress: the web frameworknpm install --omit=dev -> installeddevDependenciesneeded only to WRITE and TEST the codemocha runs tests, supertest sends test requestsnpm install --omit=dev -> skipped

Figure 2. package.json lists two kinds of libraries. dependencies, such as express, are needed to run the app on the server. devDependencies, such as mocha and supertest, are needed only while writing and testing the code; npm install --omit=dev leaves them out.

A ready-made sample app is in the GitHub repository ravibagale/nodeapp; git clone https://github.com/ravibagale/nodeapp.git downloads it.

4. Write the Express app: index.js

Why: This small file is the whole app. Knowing what each line does lets you add routes and fix errors on your own.

How: nano index.js, and write:

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello from Node.js on EC2');
});

app.get('/getusers', function (req, res) {
  res.json({ username: 'ravi' });
});

app.listen(process.env.PORT || 3000);
module.exports = app;

What each line does:

  1. require('express') loads Express from node_modules; express() creates the app object, called app.
  2. app.get('/', ...) answers a GET request for /. It is the Express version of Flask's @app.route('/').
  3. The function gets the request req and the response res. res.send() sends text or HTML.
  4. /getusers is an API route: res.json() sends the object as JSON, {"username":"ravi"}, with the content type application/json.
  5. app.listen(process.env.PORT || 3000) listens on the port in the PORT environment variable, or on 3000 if it is not set.
  6. module.exports = app lets test files, such as a supertest test, load the app.

Classroom line

Python had app.route; here it's app.get.

5. npm install and the node_modules folder

Why: require('express') fails until the library is in node_modules. npm install puts it there, at the versions package.json asks for.

How: inside ~/nodeapp:

npm install
ls

What happens:

  1. npm reads package.json, downloads Express and every library Express itself needs, and puts them all in a new folder, node_modules, inside the project.
  2. It also writes package-lock.json with the exact version of every package it installed, so the next npm install, on any server, gets the same versions.
  3. node_modules belongs to this app only. That is why Node.js needs no virtual environment: two apps in two folders each have their own node_modules.
  4. On a live server you can skip the test libraries with npm install --omit=dev.
  5. Never copy node_modules to GitHub or between servers. Put node_modules in .gitignore, keep package.json and package-lock.json, and run npm install on each server.
  6. If npm install lists vulnerabilities, npm audit shows which package; updating that package's version in package.json usually fixes it.

Classroom line

When npm install runs, it creates a folder named node_modules.

npm install fills the project's own node_modules ~/nodeapp/ package.json index.js $ npm install node_modules/ express/ body-parser/ router/ ... dozens more the helpers Express itself needs package-lock.json exact versions, for the next install ~/otherapp/ package.json node_modules/ its own libraries and versions: no clash, no venv needed Keep node_modules out of git; run npm install on each server.

Figure 3. npm install reads package.json, downloads express and all the packages it depends on into the project's own node_modules folder, and records the exact versions in package-lock.json. A second app in another folder gets its own node_modules.

6. Test the app with node index.js

Why: Testing on the server first, with curl, proves the app works before nginx or the security group can get in the way.

How:

  1. Start the app in the foreground:

    node index.js
    
  2. The prompt does not come back: the app is running in this terminal. In a second SSH window, test it on the server itself:

    curl http://127.0.0.1:3000/
    curl http://127.0.0.1:3000/getusers
    
  3. To test from your browser, add a temporary inbound rule to the security group: Custom TCP, port 3000, source My IP. Open http://PUBLIC_IP:3000/ and http://PUBLIC_IP:3000/getusers.

  4. Press Ctrl+C: the app stops. Closing the SSH window stops it too. That is fine for a test, not for a live app.

7. Keep the app running with PM2

Why: An app started with node dies when the SSH window closes. A live API must keep running and come back after a crash, which is PM2's job.

What: PM2 is a process manager for Node.js. It runs the app in the background, restarts it if it crashes, keeps its logs, and can start it again after a reboot. For Node.js it does the job gunicorn and systemd do for Python.

How:

  1. Install it once, globally (-g), so the pm2 command works in every folder:

    sudo npm install -g pm2
    
  2. Start the app from ~/nodeapp:

    pm2 start index.js
    
  3. PM2 prints a table: id 0, name index (the file name without .js), mode fork, status online. The prompt comes back, and the app keeps running after you log out.

  4. The everyday commands:

    pm2 list
    pm2 logs index
    pm2 restart index
    pm2 stop index
    pm2 delete index
    
  5. pm2 list shows the table again; the ↺ column counts restarts. pm2 logs index shows the app's output and errors live (Ctrl+C leaves the log view; the app keeps running). stop stops the app but keeps it in the list; delete removes it from PM2.

  6. To give the app a clearer name, start it with pm2 start index.js --name nodeapp and use nodeapp in the other commands.

Classroom line

To run it in the background, you need PM2.

A black terminal: [PM2] Spawning PM2 daemon with pm2_home=/home/ec2-user/.pm2, PM2 Successfully daemonized, Starting /home/ec2-user/nodeapp/index.js in fork_mode (1 instance), Done. Below, a table with the columns id, name, namespace, version, mode, pid, uptime, restarts, status, cpu, mem, user, watching and one row: 0, index, default, 0.0.1, fork, online, 0%, 33.9mb, ec2-user, disabled.

Whiteboard: pm2 start index.js: PM2 starts its daemon, starts index.js in fork mode and shows the process table with the app online.

8. Start the app again after a reboot: pm2 startup and pm2 save

Why: PM2 itself is a normal program. After a reboot, or a stop and start of the instance, nothing starts it, so the app is down until you log in.

How:

  1. Ask PM2 to create a startup service:

    pm2 startup
    
  2. It prints one command that starts with sudo env PATH=$PATH:/usr/bin ... pm2 startup systemd -u ec2-user --hp /home/ec2-user. Copy that exact line and run it. It creates a systemd service, pm2-ec2-user, that starts PM2 at boot.

  3. Save the list of running apps:

    pm2 save
    
  4. pm2 save writes the current list to ~/.pm2/dump.pm2. At boot, PM2 starts every app in that list.

  5. Test it: sudo reboot, connect again after a minute, and run pm2 list: index is online without you starting it.
  6. Run pm2 save again whenever you add or delete an app.
Keep the Node.js app running EC2 instance 1. node index.js: the app lives in your SSH session $ node index.js ^C (or SSH closed) index.js: stopped Close SSH or press Ctrl+C: the app stops. 2. pm2 start index.js: the app runs in the background PM2 (process manager, in the background) → index.js:3000 online Log out: the app stays online. 3. The app crashes: PM2 starts it again index.js crashed → restarted: ↺ 1, online again The ↺ column in pm2 list counts the restarts; pm2 logs shows why. 4. Reboot: pm2 startup + pm2 save bring it back systemd: pm2-ec2-user (starts PM2 at boot) systemd starts PM2 at boot; PM2 starts the apps saved in~/.pm2/dump.pm2.

Figure 4. Animation: node index.js in the foreground stops when SSH closes; pm2 start keeps the app online after logout; when the app crashes, PM2 restarts it; after a reboot, pm2 startup and pm2 save bring it back online.

9. Put nginx in front: proxy_pass to port 3000

Why: visitors should use plain http://ADDRESS on port 80 (and 443 for HTTPS), not port 3000. nginx receives every request and passes it on to the app. This works exactly as for a Python app; the full explanation of the reverse proxy is in Put nginx in front as the reverse proxy.

How:

  1. Install and start nginx:

    sudo yum install nginx -y
    sudo service nginx start
    
  2. Open the config with sudo nano /etc/nginx/nginx.conf. Inside the existing server { ... } block, after the root line, add:

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
    
  3. Check the file, then reload:

    sudo nginx -t
    sudo service nginx reload
    
  4. nginx -t must say syntax is ok and test is successful. If not, fix the line it names and test again; the running nginx keeps the old config meanwhile.

  5. Open http://PUBLIC_IP/ and http://PUBLIC_IP/getusers without a port: the Express app answers through nginx.

Classroom line

Now install nginx and start it.

Classroom line

Every service command needs sudo.

Close port 3000: nobody outside needs it now.

  1. Delete the port 3000 rule from the security group. Keep 22 from My IP, 80, and 443 if you add HTTPS.
  2. For extra safety, let the app listen only on the server itself. Change the last line but one of index.js to app.listen(process.env.PORT || 3000, '127.0.0.1'); and run pm2 restart index.
  3. http://PUBLIC_IP/ still works; http://PUBLIC_IP:3000/ no longer answers.
nginx in front of the Node.js appBrowserhttp://PUBLIC_IP/Security group22 My IP80 Anywhere3000 deletedonly for the testEC2 instancenginx :80location / { proxy_pass 127.0.0.1:3000;}Express appindex.js127.0.0.1:3000kept running by PM2sudo nginx -t, then sudo service nginx reload. One location / per serverblock. A stopped app gives 502 Bad Gateway.

Figure 5. The browser reaches nginx on port 80; the location / block passes the request to the Express app on 127.0.0.1:3000, kept running by PM2. The security group allows 22 and 80; the temporary port 3000 rule is deleted.

10. Change the code: pm2 restart

Why: PM2 keeps the old code in memory. Until you restart, your edit changes nothing, which looks like a bug when it isn't.

What: PM2 loaded index.js when the app started. A change to the file does nothing until the app restarts.

Worked example: add a second API route.

  1. Add a route to index.js, above the app.listen line:

    app.get('/health', function (req, res) {
      res.json({ status: 'ok' });
    });
    
  2. Open http://PUBLIC_IP/health: it says Cannot GET /health, a 404 from Express. The running app still has the old code.

  3. Restart it, and look at the log for errors:

    pm2 restart index
    pm2 logs index --lines 20
    
  4. Open /health again: it returns {"status":"ok"}.

  5. If you changed package.json, run npm install first, then pm2 restart index. pm2 restart all restarts every app.

Classroom line

Suppose you changed the code...

11. When something goes wrong

Why: Each symptom points to one layer: the app, PM2, nginx or the security group. Checking them in order finds the problem fast.

  1. node: command not found: Node.js is not installed. Run sudo yum install nodejs24 -y.
  2. Error: Cannot find module 'express': npm install was not run, or was run in another folder. cd ~/nodeapp and run npm install.
  3. npm install fails with code EJSONPARSE: package.json is not valid JSON. Look for a missing comma, a missing " or a comma after the last item.
  4. Error: listen EADDRINUSE: address already in use :::3000: another copy of the app already uses port 3000, often one started by PM2. pm2 list shows it; don't also run node index.js.
  5. http://PUBLIC_IP:3000 does not open while testing: the app is not running, or the security group has no port 3000 rule.
  6. pm2: command not found: install it with sudo npm install -g pm2.
  7. The app is gone after a reboot: run pm2 startup, run the line it prints, then pm2 save.
  8. 502 Bad Gateway: nginx works but the app does not answer. pm2 list and pm2 logs index show why; check the port in proxy_pass.
  9. The nginx welcome page instead of your app: the location / block is missing or outside the server block, or nginx was not reloaded.
  10. nginx -t says duplicate location "/": one server block can have only one location /. Delete the extra block.
  11. A new route gives Cannot GET: run pm2 restart index.

Classroom line

Only one works, not both.

Ravindra Bagale's Tip

Treat three commands as one habit: after every code change pm2 restart and a look at pm2 logs; after every nginx change sudo nginx -t before the reload; and after every new or deleted app pm2 save. And once nginx works, delete the port 3000 rule. The app only needs to be reachable through nginx.

When you're done practising, stop the EC2 instance. Everything you installed lives on the instance's EBS root volume; the next chapter adds a second EBS volume for data.

12. The whole class on one page

  1. Python and Node.js apps are hosted the same way: language, libraries, app on an internal port, a process manager, nginx in front.
  2. Amazon Linux 2023: sudo yum install nodejs24 -y (npm comes with it); Ubuntu: sudo apt install nodejs npm -y. Check with node -v and npm -v.
  3. package.json describes the app; dependencies are needed to run it, devDependencies only to write and test it.
  4. Express: app.get('/path', function (req, res) { ... }); res.send() for text, res.json() for JSON; app.listen(process.env.PORT || 3000).
  5. npm install fills the project's own node_modules and writes package-lock.json; no venv is needed. Keep node_modules out of git.
  6. node index.js runs in the foreground: good for a test with a temporary port 3000 rule.
  7. sudo npm install -g pm2, pm2 start index.js; then pm2 list, logs, restart, stop, delete.
  8. pm2 startup (run the printed line) and pm2 save bring the app back after a reboot.
  9. nginx: location / { proxy_pass http://127.0.0.1:3000; } in the server block, one location / only; sudo nginx -t, then sudo service nginx reload.
  10. Behind nginx, delete the port 3000 rule; new code needs pm2 restart index.

13. Hands-on lab: an Express API behind nginx, kept alive by PM2

Why this lab. This lab takes a fresh Amazon Linux 2023 server to a Node.js API that answers on port 80 and survives logouts and reboots, checking every layer on the way.

  1. Install Node.js
    • Command: sudo yum install nodejs24 -y
    • You should see: Complete!
    • Check: node -v prints v24.x; npm -v prints a version.
    • Why: nodejs24 is a current version that gets security fixes and brings npm with it. The plain nodejs package is Node.js 18, which no longer gets fixes.
  2. Make the project folder
    • Command: mkdir ~/nodeapp && cd ~/nodeapp
    • You should see: Nothing.
    • Check: pwd → /home/ec2-user/nodeapp
    • Why: Each app gets its own folder, so its node_modules never mixes with another app's.
  3. Write package.json
    • Do: nano package.json and enter the file from section 3, with "express" under dependencies.
    • Check: cat package.json shows the braces and commas exactly as in section 3.
    • Why: A missing comma or quote makes npm stop with a JSON parse error.
  4. Write index.js
    • Do: nano index.js and enter the code from section 4.
    • Check: cat index.js ends with app.listen(process.env.PORT || 3000);.
    • Why: Port 3000 is the app's own port; nginx will forward to it later.
  5. Install the libraries
    • Command: npm install
    • You should see: added ... packages and a new node_modules folder.
    • Check: ls node_modules/express lists Express's files.
    • Why: Without this, node index.js fails with Cannot find module 'express'.
  6. Start it with PM2
    • Command: sudo npm install -g pm2 && pm2 start index.js
    • You should see: A table with index and status online.
    • Check: curl http://127.0.0.1:3000/getusers → {"username":"ravi"}
    • Why: -g makes pm2 work in every folder. PM2 keeps the app running after you log out.
  7. Survive reboots
    • Command: pm2 startup
    • You should see: One line starting with sudo env PATH=$PATH:/usr/bin. Copy it and run it, then run pm2 save.
    • Check: sudo reboot, reconnect, pm2 list: index is online.
    • Why: startup starts PM2 at boot; save tells it which apps to start. You need both.
  8. Put nginx in front
    • Command: sudo yum install nginx -y && sudo service nginx start
    • You should see: Complete!
    • Check: Add the location / { proxy_pass http://127.0.0.1:3000; } block from section 9, then sudo nginx -t → syntax is ok and test is successful.
    • Why: Visitors use port 80; nginx passes each request to the app. nginx -t catches typos before they break the running server.
  9. Reload and test from the browser
    • Command: sudo service nginx reload
    • You should see: Nothing.
    • Check: http://PUBLIC_IP/getusers shows {"username":"ravi"} with no port in the address.
    • Why: If it fails, check that the security group allows port 80. Port 3000 never needs to be open to the internet.
  10. Change code and restart
    • Do: Add the /health route from section 10, then run pm2 restart index.
    • Check: http://PUBLIC_IP/health → {"status":"ok"}
    • Why: Before the restart the same address says Cannot GET /health, because PM2 still runs the old code.

14. Try at home

Try at home

Task 1: the app

  1. Launch an Amazon Linux 2023 instance, install nodejs24, and check node -v and npm -v.
  2. Create ~/nodeapp with package.json and index.js. Run node index.js before npm install and read the error. Then run npm install and look inside node_modules.
  3. Open port 3000 for My IP and test / and /getusers in the browser.

Task 2: PM2

  1. Start the app with PM2, close SSH, and check that the app still answers.
  2. Run pm2 startup and pm2 save, reboot the instance, and check pm2 list.
  3. Add a /health route, see Cannot GET, then pm2 restart index and see the JSON.

Task 3: nginx

  1. Install nginx, add the location / block, run sudo nginx -t, reload, and open the app on port 80.
  2. Add a second location / block, run sudo nginx -t, and read the message. Delete it again.
  3. Stop the app with pm2 stop index, see 502 Bad Gateway, and start it again. Delete the port 3000 rule.

Recap

In short

  1. A Node.js app is hosted like a Python app; package.json, npm install, node index.js and PM2 replace requirements.txt, pip, python3 app.py and gunicorn.
  2. dependencies run the app; devDependencies only help write and test it.
  3. Each project has its own node_modules, so Node.js needs no virtual environment.
  4. PM2 keeps the app running in the background; pm2 startup and pm2 save bring it back after a reboot.
  5. nginx on port 80 passes requests to 127.0.0.1:3000; test with sudo nginx -t, then close port 3000.
  6. New code needs pm2 restart.

Practice Tasks

About this scenario

Dubai Gold Souk is a jewellery business in Dubai. Fatima Al Mansoori looks after its IT and cloud work. They count money in AED and run their AWS resources in the me-central-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.

0 / 12

Easy Restart a service after a change

Why: Config changes only apply after the service restarts or reloads.

What to do: Restart the nginx service.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Easy Check if a service is running

Why: When a site is down, first check whether the web server process is running.

What to do: Show the status of the httpd service.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Easy Install a package on Ubuntu

Why: Ubuntu uses apt. Package names differ: on Ubuntu Apache is apache2.

What to do: Install the apache2 package on Ubuntu without being asked to confirm.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Easy Install a package on Amazon Linux

Why: dnf is the package manager on Amazon Linux 2023. -y answers yes for you.

What to do: Install the httpd package on Amazon Linux 2023 without being asked to confirm.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Easy Test a website from the server

Why: curl shows whether the web server answers, even before DNS is ready.

What to do: Request http://localhost and show only the response headers.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Easy Download code from GitHub

Why: git clone copies the whole repository to the server, ready to deploy.

What to do: Clone https://github.com/dubaigold/website.git into the current folder.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Medium Test the nginx config

Why: A test catches typos before a restart takes your site down.

What to do: Check the nginx configuration files for errors.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Medium Start a service now and at boot

Why: enable makes it start after reboot; --now also starts it right away.

What to do: Enable the nginx service and start it immediately, in one command.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Medium Open port 80 to the world

Why: Visitors can reach your website only if the security group allows HTTP.

What to do: Allow inbound TCP port 80 from 0.0.0.0/0 on security group sg-012bb45cc4124c3b2.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium CloudFormation: web security group

Why: Opening only 443 keeps the server safer.

What to do: Template with an AWS::EC2::SecurityGroup that allows TCP 443 from 0.0.0.0/0. Add a GroupDescription.

How:
  1. Start from the top-level keys the task names.
  2. Indent child keys with 2 spaces (no tabs).
  3. Fill in each property with the value from the task.
  4. Click Check answer. Key order does not matter.

Medium Create a security group

Why: A security group is the firewall around your instance.

What to do: Create a security group named dubaigold-web-sg with description "Web servers" in vpc-0ff45ee412deadbee.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Enable an nginx site on Ubuntu

Why: On Ubuntu, a site is enabled by a symbolic link from sites-available to sites-enabled.

What to do: Create a symbolic link to /etc/nginx/sites-available/dubaigold inside /etc/nginx/sites-enabled/.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Quick revision cards

Revise this lesson in two minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens its lesson.

Comparison card: Python Flask app vs Node.js Express app. Python Flask app: A web app written in Python. Node.js Express app: A web app written in JavaScript.
Comparison: Python Flask app vs Node.js Express app · Download PNG
Revision card: What is package.json?. A Node app needs many libraries. The server must know which ones to install. package.json lists the app name, version and dependencies. npm install reads it. Run npm install in the app folder. It creates node_modules and package-lock.json. Dependency line:
Revision card: What is package.json? · Download PNG
Revision card: A small Express app. Node needs a web framework to answer URLs easily. Express is that framework. Each route maps a URL to a function. Write index.js, then test with node index.js on port 3000. A JSON route:
Revision card: A small Express app · Download PNG
Revision card: Keep Node running with PM2. node index.js stops when you close SSH or the server reboots. PM2 is a process manager. It runs the app in the background and restarts it if it crashes. Start the app, then pm2 startup and pm2 save so it comes back after reboot. Start:
Revision card: Keep Node running with PM2 · Download PNG
Revision card: Nginx in front of Node on port 3000. Users expect port 80, and opening 3000 to the internet is unsafe. Nginx listens on 80 and proxy_passes to Node on 127.0.0.1:3000. Add the location, nginx -t, reload, then remove the port 3000 SG rule. Location:
Revision card: Nginx in front of Node on port 3000 · Download PNG
Comparison: node index.js vs pm2 start index.js. node index.js: Runs the app in your terminal. pm2 start index.js: Runs the app as a managed background process.
Comparison: node index.js vs pm2 start index.js · Download PNG
Interview card: Why use PM2 instead of node index.js in production?. PM2 keeps the app running in the background, restarts it on crash and on reboot. node index.js dies when SSH closes. Commands:
Interview card: Why use PM2 instead of node index.js in production? · Download PNG
Interview card: Nginx shows 502 Bad Gateway for a Node app. What do you check?. Whether the Node app is running and listening on the port in proxy_pass. 502 means Nginx could not get a reply from the backend. Check:
Interview card: Nginx shows 502 Bad Gateway for a Node app. What do you check? · Download PNG
Interview card: What does EADDRINUSE mean when starting Node?. The port is already used by another process, often an old copy of the app. Two programs cannot listen on the same port. Find and stop it:
Interview card: What does EADDRINUSE mean when starting Node? · Download PNG
Interview card: Why commit package-lock.json but not node_modules?. The lock file pins exact versions; node_modules can be rebuilt with npm install. node_modules is huge and OS-specific. .gitignore line:
Interview card: Why commit package-lock.json but not node_modules? · Download PNG

More cards: Ravi Sir's Cards.