π οΈ From Code to Craftsmanship: Build and Run a Node.js API on Rocky Linux Like a DevOps Blacksmith


Whether you're a DevOps beginner or a curious developer diving into APIs and backend services, this guide is your anvil and hammer. We'll shape a simple Node.js REST API and deploy it on Rocky Linux, a robust Red Hat-based distro, and even go a step further: configuring it to run as a systemd service β like an untiring background worker in your production workshop.
βοΈ Why Node.js and Rocky Linux? (The Dynamic Duo)
Picture Node.js as a nimble messenger, swiftly ferrying data between systems. Rocky Linux? Itβs your fortress, built for reliability and security. Together, they form the perfect training ground for modern DevOps workflows.
π§± Step 1: Laying the Foundation (Project Structure)
Think of this like setting up your blacksmithβs workshop β tools in place, blueprints organized, and workbenches labeled.
api_mgmt/
βββ server.js # The main control panel
βββ package.json # Toolbox inventory
βββ .env # Secret blueprints (environment variables)
βββ .gitignore # Privacy rules for Git
βββ api_mgmt.service # Instructions for our tireless worker (systemd)
βββ controllers/ # The skilled workers
β βββ healthController.js
βββ routes/ # Delivery paths
β βββ items.js
βββ models/ # Data blueprints (optional)
βββ README.md # Workshop manual
π¨ Step 2: Crafting the API (The Code)
server.js
(The Control Panel)
const express = require('express');
const morgan = require('morgan');
const cors = require('cors');
const healthController = require('./controllers/healthController');
const itemsRouter = require('./routes/items');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(morgan('dev'));
app.use(cors());
app.use(express.json());
app.get('/api/health', healthController.health);
app.use('/api/items', itemsRouter);
app.listen(PORT, () => {
console.log(`API server running on http://localhost:${PORT}`);
});
controllers/healthController.js
exports.health = (req, res) => {
res.json({ status: 'ok', message: 'API is running' });
};
routes/items.js
const express = require('express');
const router = express.Router();
let items = [];
router.get('/', (req, res) => res.json(items));
router.post('/', (req, res) => {
const item = req.body;
items.push(item);
res.status(201).json(item);
});
router.put('/:id', (req, res) => {
const id = parseInt(req.params.id);
const index = items.findIndex((item) => item.id === id);
if (index === -1) return res.status(404).json({ error: 'Item not found' });
items[index] = req.body;
res.json(items[index]);
});
router.delete('/:id', (req, res) => {
const id = parseInt(req.params.id);
items = items.filter((item) => item.id !== id);
res.status(204).send();
});
module.exports = router;
π§° Step 3: Gathering Tools (Dependencies and Configs)
.env
PORT=3000
package.json
{
"name": "api_mgmt",
"version": "1.0.0",
"description": "Simple Node.js API",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"author": "Your Name",
"license": "ISC",
"dependencies": {
"express": "^5.1.0",
"morgan": "^1.10.0",
"cors": "^2.8.5"
}
}
Install everything:
npm install
π₯ Step 4: Starting the Engine
npm start
Test the health route:
curl http://localhost:3000/api/health
Expected response:
{"status":"ok","message":"API is running"}
π§ Step 5: Running as a Service (The Tireless Worker)
Systemd is like the foreman in your smithy, making sure your API starts automatically and never misses a shift.
api_mgmt.service
Create this in /etc/systemd/system/api_mgmt.service
:
[Unit]
Description=Node.js API Management Server
After=network.target
[Service]
Type=simple
User=yourlinuxuser
WorkingDirectory=/home/yourlinuxuser/api_mgmt
ExecStart=/usr/bin/node server.js
Restart=on-failure
EnvironmentFile=/etc/api_mgmt.env
[Install]
WantedBy=multi-user.target
Copy your .env
:
sudo cp .env /etc/api_mgmt.env
sudo chmod 644 /etc/api_mgmt.env
Enable the service:
sudo systemctl daemon-reload
sudo systemctl enable --now api_mgmt
sudo systemctl status api_mgmt
π Step 6: Leveling Up Your DevOps Skills
Now that your API is humming like a machine, hereβs how you can forge it into a full-fledged production tool:
π Security β Add JWT or API key-based auth
π Monitoring β Use Grafana + Prometheus
π¦ Containerization β Dockerize your app
π Docs β Add Swagger (OpenAPI)
π CI/CD β Automate testing + deployment
β You Built a Background Warrior
You've just leveled up from "hello world" to resilient background service on Linux β the bread and butter of backend DevOps workflows.
Stay tuned, because in the next guides, we'll:
Dockerize this app
Add Swagger UI
Deploy it with GitHub Actions
π¬ Like This Guide?
I publish clean, beginner-friendly DevOps content like this weekly β practical, local-first, and hands-on.
π Subscribe to my newsletter and never miss a post!
Letβs keep building β one command, one service, and one forge-fire at a time.
Subscribe to my newsletter
Read articles from Santosh Nc directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Santosh Nc
Santosh Nc
I believe, "Hard work beats Talent when Talent doesn't work hard". A Technophile specialising in DevOps.Currently Employed at DevOps Engineer. Shaping my career with Jenkins,Docker, Automation,Poweshell, Python and other devops tools.