Category: Linux and DevOps

  • How to Create a CI/CD Pipeline: Easy DevOps Guide

    How to Create a CI/CD Pipeline: Easy DevOps Guide

    When I first explored How to Create a CI/CD Pipeline, the biggest lesson was that CI/CD is not simply another DevOps tool to configure. It is a repeatable system that turns code changes into tested, deployable software with far less manual work.

    A good pipeline can automatically install dependencies, build an application, run tests, perform security checks, create artifacts, and deploy successful releases. Instead of treating CI/CD as a complicated collection of tools, I find it easier to understand it as a sequence of automated checkpoints between writing code and releasing it.

    What Is a CI/CD Pipeline?

    CI/CD stands for continuous integration and continuous delivery or continuous deployment.

    Continuous integration involves developers regularly merging code into a shared repository. Every change can trigger automated builds and tests, allowing teams to discover integration problems early.

    Continuous delivery takes the validated application and prepares it for release. Continuous deployment goes further by automatically releasing successful changes to production when predefined requirements are satisfied.

    A typical workflow looks like this:

    Code change → Build → Test → Security checks → Package → Staging → Production

    Each stage acts as a checkpoint. When one fails, the pipeline should stop rather than allowing a potentially faulty release to progress.

    What You Need Before Building the Pipeline

    Start with a project stored in version control, usually Git. The repository may be hosted on platforms such as GitHub or GitLab.

    You will also need a CI/CD platform. Common options include GitHub Actions, GitLab CI/CD, Jenkins, CircleCI, Azure DevOps, and cloud-native deployment services.

    Before creating automation, make sure the application can already build and run its tests consistently. Automating an unreliable manual process simply produces unreliable automation.

    You should also identify the environments involved. Many projects use development, staging, and production environments with different credentials and deployment permissions.

    How to Create a CI/CD Pipeline Step by Step

    How to Create a CI-CD Pipeline Step by Step

    Building the pipeline incrementally makes debugging much easier than trying to automate the entire software lifecycle immediately.

    Step 1: Prepare the Git Repository

    Place the application in a Git repository and establish a sensible branching workflow.

    Teams commonly run validation when developers open pull or merge requests and perform additional jobs when approved code reaches the main branch.

    Avoid storing passwords, API keys, access tokens, certificates, or other credentials directly in the repository.

    Step 2: Choose a CI/CD Platform

    Select a platform that fits the repository, infrastructure, deployment environment, and team’s existing skills.

    GitHub Actions is convenient for projects already hosted on GitHub. GitLab provides tightly integrated CI/CD capabilities, while Jenkins offers extensive customization for teams that need greater control.

    The best platform is not necessarily the one with the most features. It is the one the team can operate securely and consistently, especially when they need to configure NGINX reverse proxy settings for reliable traffic management.

    Step 3: Create the Pipeline Configuration

    Modern CI/CD platforms commonly define workflows through configuration files, often using YAML.

    The configuration tells the platform what events trigger the pipeline and which jobs should execute.

    For example, a workflow might begin whenever code reaches the main branch or whenever someone opens a pull request.

    Keep the first configuration simple. Begin with one job that checks out the repository and confirms that the application can build successfully.

    Step 4: Automate the Build

    The build stage converts source code into something that can eventually be deployed.

    Depending on the project, this might involve installing dependencies, compiling source files, bundling assets, creating binaries, or building a container image.

    Use consistent runtime and dependency versions wherever possible. Reproducible builds make pipeline failures considerably easier to investigate.

    Step 5: Add Automated Tests

    Testing should happen before deployment.

    Start with fast unit tests and then introduce integration, API, or end-to-end tests where appropriate. Tests should return clear success or failure signals so the pipeline knows whether it can continue.

    Failing tests should stop the workflow immediately.

    This creates one of CI/CD’s most valuable protections: defective code is prevented from moving further through the release process.

    Step 6: Create Build Artifacts

    A successful build may produce a package, executable, archive, or container image. Store this output as a versioned artifact rather than rebuilding the application independently for every environment.

    Ideally, the artifact that passes testing should be the same artifact promoted toward production. This reduces inconsistencies between environments and improves release traceability.

    Step 7: Deploy to a Staging Environment

    Avoid making production the first environment where a release is actually deployed. Create a staging environment that closely resembles production and automatically deploy successful builds there.

    Run additional integration tests, smoke tests, or acceptance checks against staging. This helps expose problems involving infrastructure, databases, external services, or environment configuration.

    Step 8: Configure Production Deployment

    After staging validation succeeds, the application can move toward production.

    Not every project requires completely automatic production deployment. Critical systems may benefit from manual approval gates before release.

    For higher-risk applications, consider strategies such as blue-green or canary deployments. These approaches can reduce the impact of problematic releases.

    Step 9: Secure Secrets and Permissions

    Credentials should be stored using the CI/CD platform’s secrets-management capabilities rather than written directly into configuration files.

    Apply least-privilege permissions. A testing job generally should not have unrestricted production deployment credentials.

    Third-party actions, packages, plugins, and container images should also be reviewed carefully because CI/CD pipelines form part of the software supply chain.

    Step 10: Run and Verify the Complete Pipeline

    Trigger the complete workflow and watch every stage.

    Check that failed builds stop deployment, tests produce understandable results, artifacts are correctly versioned, staging works as expected, and production deployment uses the intended release.

    Also test failure scenarios deliberately. A pipeline is useful only when it responds safely when something goes wrong.

    Make the CI/CD Pipeline Faster

    Pipeline speed directly affects developer productivity.

    Cache dependencies when appropriate instead of downloading them during every run. Independent tests can often execute in parallel, while unchanged components may not need to be rebuilt repeatedly.

    However, speed should not come at the expense of reliability.

    A slightly slower pipeline that consistently catches defects is more valuable than an extremely fast workflow that allows broken releases through.

    Monitor CI/CD Pipeline Performance

    Automation should be measured after implementation.

    Useful indicators include deployment frequency, lead time for changes, change failure rate, and recovery time after failed releases. These measurements can reveal whether the delivery process is actually improving.

    Pipeline-specific metrics such as build duration, test failure frequency, deployment success rate, and queue time can reveal additional bottlenecks.

    Common CI/CD Pipeline Problems

    Common CI-CD Pipeline Problems

    One frequent mistake is creating an enormous pipeline immediately. Start with build and test automation, verify that it works, and introduce deployment stages gradually.

    Another issue is allowing development and production environments to drift apart. Containers and infrastructure-as-code practices can help make environments more consistent.

    Flaky tests are equally damaging. Developers eventually stop trusting pipelines that fail unpredictably.

    Finally, avoid complicated configuration duplication. Reusable workflows, templates, variables, and shared jobs can make larger pipelines easier to maintain.

    CI/CD Pipeline Best Practices

    Keep jobs small enough that failures are easy to identify. Run fast checks early and expensive tests later. Protect production credentials, restrict permissions, and maintain clear separation between staging and production.

    Make deployments observable as well. Application logs, infrastructure monitoring, health checks, and alerts should quickly reveal whether a newly deployed version is operating correctly.

    Most importantly, design rollback procedures before they are needed. Deployment automation without a recovery strategy leaves an important part of the release process unfinished.

    Frequently Asked Questions

    1. What is the easiest way to learn How to Create a CI/CD Pipeline?

    Start with a small application and automate only its build and unit tests. Once those stages work reliably, add artifacts, staging deployment, security controls, and finally production deployment.

    2. Which tool is best for beginners?

    GitHub Actions can be approachable for projects already stored on GitHub because repository events, workflow configuration, secrets, and automation are available within the same ecosystem.

    3. Should CI/CD automatically deploy to production?

    Not necessarily. Continuous delivery can prepare software for release while retaining a manual approval step. Continuous deployment automatically releases changes that successfully pass every required check.

    4. What stages should a CI/CD pipeline contain?

    A practical pipeline commonly includes source control, dependency installation, build automation, testing, security checks, artifact creation, staging validation, production deployment, and post-deployment monitoring.

    From Commit to Confident Release

    When I look at a successful CI/CD workflow, I see much more than automated deployment. I see a safety system that gives developers rapid feedback and creates a repeatable path from a code change to a reliable release.

    The most effective approach is to begin with a small pipeline, make every stage dependable, and expand it gradually. Once build automation, testing, artifacts, staging, security, deployment, monitoring, and rollback work together, releasing software becomes far more predictable and manageable.

  • How to Secure SSH on Ubuntu Server Without Getting Locked Out

    How to Secure SSH on Ubuntu Server Without Getting Locked Out

    The first time I hardened SSH on a server, my biggest concern was not an attacker. It was accidentally locking myself out. SSH is one of the most useful administration tools on Ubuntu, but because it provides remote command-line access, weak authentication or careless configuration can expose a server to unnecessary risk.

    Learning How to Secure SSH on Ubuntu Server means strengthening authentication, restricting unnecessary access, filtering network traffic, monitoring login activity, and testing every change before closing your working connection. The safest approach is layered security rather than relying on one trick such as changing the default SSH port.

    Why SSH Security Matters on Ubuntu

    Internet-facing SSH servers are constantly scanned by automated systems searching for weak passwords, exposed root accounts, outdated software, and common configuration mistakes.

    A secure SSH setup therefore starts with reducing the number of ways an attacker can authenticate. Strong SSH keys, restricted accounts, firewall controls, login limits, and server monitoring work together to provide far stronger protection than passwords alone.

    Ubuntu uses OpenSSH for remote administration. Its configuration is powerful, but even a small syntax error can interrupt remote access. That makes testing and configuration validation essential parts of SSH hardening.

    Prepare Your Ubuntu Server Before SSH Hardening

    Prepare Your Ubuntu Server Before SSH Hardening

    Before modifying SSH, update your installed packages:

    sudo apt update

    sudo apt upgrade

    Security updates can patch vulnerabilities in OpenSSH and related system components.

    You should also avoid making major SSH changes while relying on a single active connection. Keep your existing terminal open and create a second session whenever testing authentication changes. If the new connection fails, your original session gives you a way to repair the configuration.

    Create a Non-Root Administrative User

    Logging directly into the root account increases risk because attackers already know the username they need to target.

    Create a regular account and grant it administrative privileges:

    sudo adduser adminuser

    sudo usermod -aG sudo adminuser

    Verify that the new account can log in and run commands through sudo before restricting root access.

    Using individual administrative accounts also makes activity easier to trace when several people manage the same server.

    Set Up SSH Key Authentication

    SSH keys are significantly harder to guess or brute-force than ordinary passwords.

    On your local computer, generate an Ed25519 key:

    ssh-keygen -t ed25519

    Use a strong passphrase when practical. The private key should remain only on your trusted device.

    Copy the public key to your Ubuntu server:

    ssh-copy-id adminuser@server-ip

    Open another terminal and verify that key-based authentication works successfully before changing password settings.

    Disable SSH Password Authentication

    Disable SSH Password Authentication

    Once key authentication works reliably, password login can be disabled.

    Ubuntu supports the main configuration file:

    /etc/ssh/sshd_config

    Modern Ubuntu installations can also use custom configuration snippets inside:

    /etc/ssh/sshd_config.d/

    Using a dedicated configuration snippet can make your security changes easier to maintain.

    Configure:

    PasswordAuthentication no

    PubkeyAuthentication yes

    Never disable passwords until you have confirmed that your SSH key works from another terminal.

    Disable Direct Root SSH Login

    Direct root authentication should normally be disabled on remotely administered servers; administrators should use a secure account with elevated privileges to restart services using Systemctl when needed.

    Add:

    PermitRootLogin no

    Administrators can instead connect through their regular accounts and use sudo when privileged commands are required.

    This removes a predictable high-value login target while improving accountability.

    Restrict Which Users Can Use SSH

    If only specific accounts need remote access, explicitly permit them.

    For example:

    AllowUsers adminuser

    You can also use AllowGroups when several authorized administrators belong to a dedicated group.

    Restricting SSH access reduces the number of valid accounts an attacker can target.

    Reduce Authentication Attempts

    Reduce Authentication Attempts

    OpenSSH provides settings that can make repeated login attempts less effective.

    Consider:

    MaxAuthTries 3

    LoginGraceTime 30

    MaxAuthTries limits authentication attempts per connection, while LoginGraceTime controls how long users have to authenticate.

    Avoid extremely restrictive values that could inconvenience legitimate administrators.

    Validate SSH Configuration Before Restarting

    One of the most important SSH security practices is checking your configuration before applying it.

    Run:

    sudo sshd -t

    If no configuration errors appear, reload or restart SSH:

    sudo systemctl restart ssh

    Do not immediately close your original session. Open another connection and confirm that everything still works.

    Protect SSH With a Firewall

    Ubuntu’s UFW firewall can restrict incoming SSH traffic.

    Enable SSH access before activating the firewall:

    sudo ufw allow OpenSSH

    sudo ufw enable

    sudo ufw status

    When administrators connect from predictable networks, restricting SSH to trusted IP addresses offers even stronger protection.

    Firewall filtering reduces unnecessary exposure before authentication even begins.

    Use Fail2Ban Against Repeated Login Attempts

    Use Fail2Ban Against Repeated Login Attempts

    Fail2Ban monitors logs and temporarily blocks IP addresses displaying suspicious behavior.

    Install it using:

    sudo apt install fail2ban

    A properly configured SSH jail can help reduce automated password attacks and excessive connection attempts.

    However, Fail2Ban should complement strong authentication rather than replace SSH keys or firewall restrictions.

    Should You Change SSH Port 22?

    Changing the default SSH port may reduce automated scans and noisy logs because many bots initially probe port 22.

    It should not be treated as a primary security control.

    Attackers can scan alternative ports, so SSH keys, disabled passwords, restricted users, firewall rules, and monitoring remain far more important.

    Consider Two-Factor Authentication

    Servers containing particularly sensitive systems can add another authentication factor.

    Two-factor authentication can require something the administrator possesses in addition to a key or password. Depending on the environment, this may involve authentication applications or hardware-backed security devices.

    Advanced environments may also restrict SSH behind VPNs, bastion hosts, or private network access.

    Monitor SSH Login Activity

    Hardening should continue after configuration.

    You can inspect SSH service activity using:

    sudo journalctl -u ssh

    Authentication information may also be available through:

    /var/log/auth.log

    Look for repeated failed logins, unfamiliar users, unexpected source addresses, or unusual login times.

    Regular monitoring can reveal suspicious behavior that preventive controls alone might not stop.

    What to Do If SSH Stops Working

    What to Do If SSH Stops Working

    If a new SSH session fails, keep your existing connection open.

    Check configuration syntax again:

    sudo sshd -t

    Then inspect service status:

    sudo systemctl status ssh

    Review recent SSH logs for authentication or configuration errors.

    If your hosting environment offers a recovery console, keep its access details available before performing major SSH changes.

    Frequently Asked Questions

    1. What is the safest way to secure SSH on Ubuntu?

    The safest approach combines key-based authentication, disabled root access, restricted users, firewall rules, configuration validation, updates, monitoring, and careful testing before disconnecting existing sessions.

    2. Should I disable SSH password authentication?

    Yes, once SSH key authentication has been successfully configured and tested. Disabling passwords greatly reduces exposure to password guessing and automated brute-force attempts.

    3. Is Fail2Ban necessary when SSH keys are enabled?

    Not always. SSH keys already provide strong protection, but Fail2Ban can still reduce unwanted connection attempts, log noise, and abusive automated traffic.

    4. How to Secure SSH on Ubuntu Server without locking yourself out?

    Keep an existing SSH connection open, test key authentication in a second terminal, run sshd -t before restarting SSH, and confirm a fresh connection works before disconnecting.

    A Safer SSH Setup Starts With Layers

    When I secure an Ubuntu server, I never depend on one setting. I treat SSH security as a layered process: strong keys protect authentication, account restrictions reduce exposure, firewalls control network access, Fail2Ban limits abusive behavior, and monitoring helps detect unusual activity.

    The most important lesson in How to Secure SSH on Ubuntu Server is to make every security change carefully and verify it before moving forward. A hardened SSH configuration is useful only when authorized administrators can still reach the machine safely.

  • How to Restart Services Using Systemctl Without Breaking Linux

    How to Restart Services Using Systemctl Without Breaking Linux

    When I make changes to a Linux server, restarting the affected service is often the fastest way to apply them. However, running the wrong command or restarting a service without checking its configuration can create unnecessary downtime.

    The basic command is simple:

    sudo systemctl restart service-name

    Replace service-name with the actual unit you want to restart, such as nginx, apache2, httpd, mysql, or sshd. In this guide, I will show you how to restart a service, verify that it is running, inspect errors, and avoid common mistakes.

    What Does the systemctl Restart Command Do?

    The systemctl command controls services managed by systemd, the service manager used by most modern Linux distributions.

    When you run:

    sudo systemctl restart nginx

    systemd stops the Nginx service and then starts it again. This process allows the application to load configuration changes, clear temporary problems, or recover from an unresponsive state.

    A restart may briefly interrupt the service. For a web server, this could produce a short period when requests cannot be handled. That is why it is important to validate configuration files before restarting critical services.

    Find the Correct Service Name First

    Find the Correct Service Name First

    A common reason a restart command fails is that the wrong service name was entered. Service names may also differ between Linux distributions.

    For example, Apache is commonly called apache2 on Debian-based systems. On Red Hat-based systems, it is generally called httpd.

    You can search installed service units with:

    systemctl list-unit-files –type=service

    To view currently loaded services, run:

    systemctl list-units –type=service

    You can narrow the results with grep:

    systemctl list-units –type=service | grep -i nginx

    Once you identify the correct unit, use its name in the restart command. Adding .service is optional in most cases, so nginx and nginx.service usually produce the same result.

    Restart a Service Step by Step

    Check Its Current Status

    Before making changes, check whether the service is active, inactive, or already failing:

    sudo systemctl status nginx

    The output normally shows its current state, process ID, recent activity, and a few log entries.

    You can request a shorter response with:

    systemctl is-active nginx

    Typical results include active, inactive, failed, or activating.

    Restart the Service

    Run the restart command after confirming the correct service name:

    sudo systemctl restart nginx

    A successful command normally produces no terminal output. Silence does not automatically confirm that the application is healthy, so verification is still required.

    Verify the Restart

    Check the status again:

    sudo systemctl status nginx

    You should see active (running) when the restart succeeds. For a script-friendly check, use:

    systemctl is-active nginx

    For network applications, you should also test the actual service. A running web server unit, for example, might still have an application-level or connectivity problem.

    Common Restart Examples

    Common Restart Examples

    To restart Nginx:

    sudo systemctl restart nginx

    To restart Apache on Fedora, Rocky Linux, AlmaLinux, or RHEL:

    To restart Apache on Ubuntu or Debian:

    sudo systemctl restart apache2

    To restart Apache on Fedora, Rocky Linux, AlmaLinux, or RHEL:

    sudo systemctl restart httpd

    To restart MySQL:

    sudo systemctl restart mysql

    Some systems may use:

    sudo systemctl restart mysqld

    To restart the OpenSSH server, the unit may be named ssh or sshd:

    sudo systemctl restart ssh

    sudo systemctl restart sshd

    Be careful when restarting SSH on a remote server. Keep the current session open, validate the configuration, and test a second connection before closing your existing terminal.

    Restart Versus Reload

    Restart and reload are not identical operations.

    A restart stops and starts the process:

    sudo systemctl restart nginx

    A reload asks the running application to reread its configuration without fully stopping:

    sudo systemctl reload nginx

    Reloading may reduce interruption, but it works only when the service supports reload operations.

    A useful alternative is:

    sudo systemctl reload-or-restart nginx

    This reloads the service when possible and restarts it when reload support is unavailable.

    When to Use daemon-reload

    When to Use daemon-reload

    The daemon-reload command is often confused with a service restart:

    sudo systemctl daemon-reload

    This command tells systemd to reread unit files after creating or modifying a service file, but it does not restart the application or monitor running process in Linux.

    After editing a custom unit file, use:

    sudo systemctl daemon-reload

    sudo systemctl restart myapp.service

    The first command reloads systemd’s definitions. The second restarts the application using the updated unit configuration.

    Validate Configuration Before Restarting

    A restart can cause a working service to fail when its configuration contains an error. Many applications provide validation commands.

    For Nginx:

    sudo nginx -t

    For Apache:

    sudo apachectl configtest

    For OpenSSH:

    sudo sshd -t

    If validation reports an error, fix it before restarting. This small step is especially important for production servers and remote systems.

    Fix a Service That Will Not Restart

    Fix a Service That Will Not Restart

    Start by examining the full status output:

    sudo systemctl status service-name

    Then inspect recent logs:

    sudo journalctl -u service-name –since “10 minutes ago”

    For detailed errors related to a failed unit, run:

    sudo journalctl -xeu service-name

    Common causes include invalid configuration syntax, incorrect file permissions, missing directories, unavailable dependencies, port conflicts, and incorrect environment variables.

    You can check whether another process is using a required port with:

    sudo ss -lntp

    After correcting the underlying issue, clear the failed state when necessary:

    sudo systemctl reset-failed service-name

    Then attempt the restart again.

    Frequently Asked Questions

    1. How do I use How to Restart Services Using Systemctl safely?

    Check the current status, validate the application configuration, restart the correct unit, and verify both the service state and application response afterward.

    2. Do I need sudo to restart a service?

    Most system services require administrative privileges, so regular users generally need to place sudo before the command.

    3. Does restarting a service enable it at boot?

    No. Restarting affects the current session only. To enable a service at startup, run:

    sudo systemctl enable service-name

    To enable and start it immediately, use:

    sudo systemctl enable –now service-name

    4. Why does systemctl restart show no output?

    A successful command commonly returns without a message. Use systemctl status, systemctl is-active, and application-level testing to confirm success.

    The Final Command Check

    I rely on How to Restart Services Using Systemctl as a simple workflow rather than a single command. I first confirm the unit name, check its current condition, validate configuration changes, restart it, and then verify the result.

    That approach prevents many avoidable outages. When a restart fails, the status output and journalctl logs usually reveal the real cause. The restart command may be short, but careful verification is what makes Linux service management reliable.

  • How to Build a DevOps Home Lab: Beginner Setup Guide

    How to Build a DevOps Home Lab: Beginner Setup Guide

    Building a personal lab changed the way I understood DevOps. Reading about containers, infrastructure automation, CI/CD, and Kubernetes helped, but actually deploying them made everything click. Learning How to Build a DevOps Home Lab gives you a safe environment where mistakes become useful lessons instead of costly production problems.

    A good lab does not require a rack full of expensive servers. You can begin with an old desktop, spare laptop, mini PC, or reasonably powerful workstation and expand only when your projects demand more resources.

    What Is a DevOps Home Lab?

    A DevOps home lab is a private environment where you can experiment with technologies used to build, deploy, automate, secure, and monitor applications.

    Instead of simply watching tutorials, you create infrastructure and troubleshoot real problems yourself.

    What Can You Learn?

    A well-designed lab can help you practise Linux administration, Git, Docker, networking, infrastructure as code, configuration management, CI/CD pipelines, know about Kubernetes, monitoring, security, and disaster recovery.

    More importantly, you learn how these technologies work together.

    Home Lab vs Cloud Lab

    Cloud platforms are excellent for learning, but costs can increase when virtual machines, storage, databases, and Kubernetes clusters remain active.

    Local infrastructure gives you greater freedom to experiment without constantly watching usage charges.

    A hybrid approach is also useful. Run your main environment locally while occasionally deploying projects to a cloud provider to understand real cloud workflows.

    Choose the Right Hardware

    Choose the Right Hardware

    You do not need enterprise equipment to get started.

    For a basic Linux and Docker environment, 8 to 16 GB of RAM can work well. Moving toward multiple virtual machines, Kubernetes, monitoring, and CI services becomes easier with 16 to 32 GB.

    A machine with 32 to 64 GB gives considerably more flexibility for larger clusters.

    CPU and Storage

    Choose a processor with multiple cores and hardware virtualization support.

    SSD or NVMe storage is strongly recommended because virtual machines and containers generate frequent disk activity. Traditional hard drives can still work for backups or bulk storage.

    Consider Power and Noise

    Old enterprise servers can appear inexpensive, but electricity use, heat, and fan noise may make them inconvenient.

    Energy-efficient mini PCs are often more practical for a home environment because they can remain online continuously without consuming excessive power.

    Build the Lab in the Right Order

    One common mistake is installing every popular DevOps tool immediately.

    A better approach is to build your environment gradually.

    Start with Linux and networking. Add containers next. Then introduce configuration management, infrastructure automation, CI/CD, orchestration, monitoring, and finally GitOps.

    Understanding each layer makes troubleshooting dramatically easier.

    Step 1: Install Linux and Configure SSH

    Step 1 - Install Linux and Configure SSH

    Start with a Linux distribution such as Ubuntu Server or Debian.

    Learn essential commands for navigating directories, managing permissions, installing packages, checking logs, managing processes, and configuring services.

    Next, configure SSH so you can manage machines remotely.

    Use SSH keys rather than repeatedly entering passwords. This also introduces an authentication method commonly used in automated infrastructure.

    Step 2: Create Virtual Machines

    Virtualization allows one physical computer to behave like several independent servers.

    Proxmox VE is a popular choice for dedicated home lab machines because it lets you create and manage virtual machines and Linux containers through a web interface.

    You can create separate machines for applications, Kubernetes nodes, monitoring, automation, and testing.

    Beginners using their everyday computer can alternatively experiment with VirtualBox, VMware, or similar virtualization software.

    Step 3: Learn Docker Containers

    Once Linux feels comfortable, move into containers.

    Install Docker and deploy a simple web application. Learn how images, containers, ports, volumes, and networks work.

    Then experiment with Docker Compose.

    For example, you could deploy an application using separate containers for the frontend, backend, and database. This teaches service communication while remaining easier to understand than Kubernetes.

    Step 4: Automate Configuration With Ansible

    Step 4 - Automate Configuration With Ansible

    Manually configuring five servers quickly becomes repetitive.

    Ansible lets you describe configuration tasks in reusable playbooks.

    You can automate jobs such as installing Docker, creating users, updating packages, copying configuration files, enabling services, and applying common security settings.

    Try destroying a virtual machine and rebuilding its configuration automatically. That exercise demonstrates why automation matters.

    Step 5: Manage Infrastructure as Code

    Terraform or OpenTofu can introduce infrastructure as code principles.

    Instead of clicking through interfaces every time you need infrastructure, you define resources using configuration files stored in Git.

    Your home lab becomes increasingly reproducible.

    Combine infrastructure provisioning with Ansible configuration management so one tool creates the infrastructure while another configures the operating systems and applications, making it easier to monitor servers with Prometheus across the environment.

    Step 6: Build a CI/CD Pipeline

    The next step is automating software delivery.

    Create a small application, store its code in Git, and connect it to GitHub Actions, GitLab CI, Jenkins, Gitea, or another CI platform.

    Build a pipeline that automatically:

    checks code changes, runs tests, builds a container image, pushes the image to a registry, and deploys the updated application.

    This transforms your lab from a collection of servers into a real DevOps workflow.

    Step 7: Create a Kubernetes Home Lab

    Step 7 - Create a Kubernetes Home Lab

    Kubernetes should generally come after Docker rather than before it.

    For a home environment, lightweight distributions such as k3s can reduce hardware requirements.

    Create one control-plane node and one or more worker nodes using virtual machines.

    Practise deployments, services, namespaces, ConfigMaps, Secrets, persistent storage, rolling updates, and scaling.

    Once the basics become comfortable, add Helm for application packaging.

    Step 8: Add Monitoring and Observability

    A production-like environment should tell you when something is wrong.

    Prometheus can collect metrics while Grafana turns those metrics into useful dashboards.

    Monitor CPU usage, memory consumption, disk space, container health, application availability, and Kubernetes resources.

    You can later add centralized logging and alerts.

    Try deliberately stopping an application and watching how your monitoring system responds.

    Secure Your DevOps Home Lab

    Security should be part of the architecture rather than something added at the end.

    Use SSH keys, apply operating-system updates, configure firewalls, remove unnecessary services, restrict administrative permissions, and keep secrets outside source code.

    As your network grows, consider separating workloads with VLANs.

    For remote access, a VPN is generally safer than exposing administrative interfaces directly to the internet.

    Kubernetes users should also learn RBAC, NetworkPolicies, secret management, and least-privilege permissions.

    Configure Networking Properly

    Configure Networking Properly

    Networking causes many home lab problems, so learning the basics pays off quickly.

    Understand DHCP, static addresses, DNS, NAT, bridged networking, ports, subnets, and firewalls.

    A reverse proxy such as Traefik or Nginx can route different domain names to internal applications.

    You can later configure HTTPS certificates so services use encrypted connections.

    Document your network addresses and hostnames. Good documentation becomes increasingly valuable as the environment grows.

    Create a Backup and Recovery Plan

    A lab should also teach what happens after failure.

    Back up important configuration files, virtual machines, container data, databases, and infrastructure definitions.

    Then test your backups.

    A useful exercise is intentionally deleting a disposable virtual machine, recreating it with infrastructure automation, restoring its application data, and confirming that everything works again.

    Recovery testing turns backups from an assumption into a proven process.

    DevOps Home Lab Projects to Try

    Once the foundation is working, build projects that connect multiple skills together.

    Create a containerized web application that automatically deploys after a Git push.

    Build a Kubernetes cluster provisioned through infrastructure as code.

    Configure Ansible to manage several Linux machines.

    Deploy Prometheus and Grafana dashboards.

    Create separate development, staging, and production-like environments.

    You can also experiment with GitOps tools such as Argo CD or Flux so changes stored in Git automatically update the cluster.

    These projects are useful portfolio examples because they demonstrate complete workflows rather than isolated commands.

    A Practical Learning Roadmap

    A Practical Learning Roadmap

    Start small and expand naturally.

    Learn Linux and SSH first, followed by Git and networking. Add Docker and Docker Compose once you understand the operating system.

    Then move into Ansible and infrastructure as code.

    Build a CI/CD pipeline before introducing Kubernetes.

    After Kubernetes is stable, add monitoring, logging, security controls, backups, and GitOps.

    Following this progression makes each new technology solve a problem you already understand.

    Frequently Asked Questions

    1. How Much RAM Do I Need for a DevOps Home Lab?

    Around 8 to 16 GB can support basic Linux and Docker learning, while 32 GB or more provides greater flexibility for multiple virtual machines, Kubernetes, CI/CD, and monitoring.

    2. Can I Use an Old Laptop for a DevOps Lab?

    Yes. An old laptop can be excellent for Linux, Docker, Git, automation, networking, and lightweight Kubernetes experimentation.

    3. Do I Need Kubernetes in My Home Lab?

    No. Learn Linux, networking, Git, and containers first. Kubernetes becomes much easier once those fundamentals are familiar.

    4. Is How to Build a DevOps Home Lab Useful for Learning DevOps?

    Yes. A home lab lets you practise infrastructure, automation, CI/CD, containers, monitoring, networking, security, and recovery using real systems instead of only studying theory.

    Final Thoughts

    Building my own environment taught me that the most valuable home lab is not the one with the most servers or the longest list of tools. It is the one I can understand, rebuild, automate, break, monitor, and recover.

    Start with one machine and a few Linux virtual machines. Add Docker, automation, CI/CD, Kubernetes, and observability only as your skills develop.

    Over time, that modest setup can become a realistic platform for testing the same ideas used in professional DevOps environments.

  • How to Monitor Running Processes in Linux and Fix Lags

    How to Monitor Running Processes in Linux and Fix Lags

    When a Linux computer becomes slow, overheats, freezes, or stops responding normally, I usually check its active processes before changing configurations or restarting the machine. A single application may be consuming too much memory, a background service may be stuck, or an unexpected process may be using most of the available CPU.

    Learning How to Monitor Running Processes in Linux gives you a direct view of what the operating system is doing. Linux includes several built-in commands for viewing process IDs, resource consumption, process states, parent-child relationships, and system services. Some commands provide a one-time snapshot, while others update continuously.

    Understanding Linux Processes and PIDs

    A process is an active instance of a program. Opening a browser, starting a web server, running a script, or launching a terminal creates one or more processes.

    Every process receives a unique process ID, commonly called a PID. Linux uses this number to track and manage the process. Processes may also have a parent process ID, which identifies the process that started them.

    Common process states include running, sleeping, stopped, zombie, and uninterruptible sleep. A sleeping process is not necessarily a problem. Many background services remain asleep until they receive work. A zombie process, however, has completed but has not been properly collected by its parent.

    Use ps to View a Process Snapshot

    Use ps to View a Process Snapshot

    The ps command displays a snapshot of processes at the moment the command runs. It does not update continuously, making it useful for reports, scripts, and quick inspections.

    Run the following command to see processes associated with the current terminal:

    ps

    For a more complete view, use:

    ps aux

    This version displays processes from all users along with CPU usage, memory usage, PID, start time, status, and command information.

    The %CPU column shows processor consumption, while %MEM shows the percentage of physical memory being used. The STAT column indicates the process state.

    Sort Processes by CPU Usage

    To identify applications consuming the most processor time, run:

    ps aux –sort=-%cpu | head

    The minus sign sorts the results from highest to lowest. This command is especially useful when a machine suddenly becomes slow or its fans begin running heavily.

    Sort Processes by Memory Usage

    To find the largest memory consumers, use:

    ps aux –sort=-%mem | head

    A process using significant memory is not automatically faulty. Databases, browsers, virtual machines, and development tools may legitimately require large amounts of RAM. Investigate unusual growth or consumption that affects other applications.

    Use top for Real-Time Monitoring

    The top command provides a continuously updating view of system activity:

    top

    The upper section shows load averages, task counts, CPU activity, memory usage, and swap usage. The lower section lists individual processes.

    Inside top, press P to sort by CPU consumption and M to sort by memory consumption. Press k to send a signal to a process, r to change its priority, and q to exit.

    Load average represents the amount of work waiting for or using system resources over one, five, and fifteen minutes. A consistently high load may indicate CPU pressure, blocked disk operations, or too many competing tasks.

    Use htop for an Interactive View

    Use htop for an Interactive View

    The htop utility provides a visual, user-friendly way to monitor system activity, while Linux administration tools let you create users and groups to manage access and permissions.

    On Debian or Ubuntu systems, install it with:

    sudo apt install htop

    On Fedora or similar distributions, use:

    sudo dnf install htop

    Then start it by running:

    htop

    You can navigate with the keyboard, search for processes, display processes as a tree, change priorities, and send termination signals. Although htop is convenient, it may not be installed by default on minimal servers.

    Find a Specific Process with pgrep

    When you know the application or service name, pgrep is faster than reading a long process list.

    pgrep nginx

    To display both the PID and command name, use:

    pgrep -a nginx

    You can also use pidof for programs that are already running:

    pidof nginx

    Another common method combines ps with grep:

    ps aux | grep nginx

    However, this may include the grep command itself. pgrep usually produces cleaner results.

    View Parent and Child Processes with pstree

    The htop utility provides a more visual and user-friendly alternative to top. It uses colored meters, supports scrolling, and makes searching or filtering easier.

    On Debian or Ubuntu systems, install it with:

    sudo apt install htop

    On Fedora or similar distributions, use:

    sudo dnf install htop

    Then start it by running:

    htop

    You can navigate with the keyboard, search for processes, display processes as a tree, change priorities, and send termination signals. Although htop is convenient, it may not be installed by default on minimal servers.

    Find a Specific Process with pgrep

    When you know the application or service name, pgrep is faster than reading a long process list and can help you quickly check per process user activity.

    pgrep nginx

    To display both the PID and command name, use:

    pgrep -a nginx

    You can also use pidof for programs that are already running:

    pidof nginx

    Another common method combines ps with grep:

    ps aux | grep nginx

    However, this may include the grep command itself. pgrep usually produces cleaner results.

    Monitor Services Managed by systemd

    Monitor Services Managed by systemd

    Many background applications run as systemd services. Check a service with:

    systemctl status nginx

    This displays its current state, main PID, recent log messages, and resource information.

    For more detailed logs, run:

    journalctl -u nginx

    To follow new entries continuously, add the -f option:

    journalctl -u nginx -f

    Monitoring both the process and its logs provides more context than relying on CPU or memory figures alone.

    Stop a Problematic Process Safely

    Once you identify a faulty process, try a normal termination signal first:

    kill PID

    Replace PID with the actual process ID. This sends SIGTERM, allowing the program to perform cleanup before closing.

    Use forceful termination only when the process ignores the normal signal:

    kill -9 PID

    You can terminate processes by name with pkill, but use it carefully because multiple matching processes may be affected.

    For systemd services, restarting through systemd is generally safer:

    sudo systemctl restart nginx

    Frequently Asked Questions

    1. What is the easiest way to learn How to Monitor Running Processes in Linux?

    Start with ps aux for a one-time snapshot, top for live updates, and htop for a more interactive interface.

    2. How can I monitor only one Linux process?

    Find its PID with pgrep, then use top -p PID or pidstat -p PID to focus on that process.

    3. How do I identify a zombie process?

    Run ps aux and look for Z in the STAT column. You normally need to address its parent process rather than killing the zombie directly.

    The Final Check Before You Restart Everything

    I prefer investigating the active workload with a one time snapshot before restarting an entire Linux system. A restart may temporarily hide the symptom without revealing the process, service, or application responsible for it.

    My usual workflow begins with ps aux, moves to top or htop for live activity, and then uses pgrep, pstree, pidstat, or iotop for deeper investigation. When a service is involved, I check both systemctl and journalctl before taking action.

    Once you understand How to Monitor Running Processes in Linux, performance problems become easier to isolate, explain, and resolve without unnecessary disruption.

  • How to Create Users and Groups in Linux Easily

    How to Create Users and Groups in Linux Easily

    Managing accounts is one of the first administrative skills I learned when working with Linux servers. Whether I am preparing a development environment, granting a colleague access, or separating permissions between teams, users and groups help me control who can access files, applications, and system resources.

    In this guide, I will explain How to Create Users and Groups in Linux using practical terminal commands. I will also cover primary and supplementary groups, password creation, home directories, account verification, administrative privileges, shared folders, deletion, and common errors.

    What Are Linux Users and Groups?

    A Linux user is an account that can own files, run programs, and access system resources. Each user receives a unique numerical user ID, commonly called a UID.

    A group is a collection of users who share certain permissions. Groups make administration easier because I can grant access to several users at once instead of changing permissions for every account individually.

    Linux normally stores account information in these files:

    • /etc/passwd contains basic user details.
    • /etc/group contains group names and memberships.
    • /etc/gshadow stores protected group information.

    These files should not normally be edited manually. Linux provides commands that update them safely.

    Primary and Supplementary Groups

    Every Linux user has one primary group. Files created by that user are usually assigned to this group automatically.

    A user can also belong to several supplementary groups. These additional memberships provide access to shared folders, administrative commands, applications, hardware, or services.

    The -g option assigns a primary group, while -G assigns supplementary groups.

    Check Existing Users and Groups First

    Check Existing Users and Groups First

    Before creating an account, I check whether the username or group already exists.

    getent passwd alex

    getent group developers

    If the commands return no output, the names are probably available.

    I can also inspect all local account and group records with:

    cat /etc/passwd

    cat /etc/group

    Using getent is generally safer because it can display accounts from local files and connected identity services.

    Create a New Group in Linux

    To create a group named developers, run:

    sudo groupadd developers

    Verify that the group was created:

    getent group developers

    The output should display the group name and its assigned group ID.

    Create a Group With a Specific GID

    Linux normally assigns the next available group ID automatically. When matching permissions across several systems, I may need a specific GID.

    sudo groupadd -g 2500 developers

    The selected number must not already belong to another group. Check it before proceeding:

    getent group 2500

    Create a New Linux User

    Create a New Linux User

    The useradd command creates a user account. I include -m so Linux also creates a home directory.

    sudo useradd -m alex

    Set a secure password:

    sudo passwd alex

    The terminal will ask for the new password twice. Password characters will not appear while typing, which is normal.

    Choose a Login Shell

    To create the account with Bash as its login shell, run:

    sudo useradd -m -s /bin/bash alex

    The -s option defines the program that starts when the user opens a terminal session.

    For a service account that should not allow interactive login, use:

    sudo useradd -r -s /usr/sbin/nologin appservice

    The -r option creates a system account.

    Create a User With a Primary Group

    To create a user and assign developers as the primary group, run:

    sudo useradd -m -s /bin/bash -g developers alex

    sudo passwd alex

    The group must exist before running this command.

    Verify the result:

    id alex

    The output displays the user’s UID, primary GID, and supplementary memberships.

    Add an Existing User to a Group

    Add an Existing User to a Group

    To add an existing user to a supplementary group, use:

    sudo usermod -aG developers alex

    The -a option means append, while -G specifies supplementary groups.

    Never omit -a unless you intentionally want to replace the user’s existing supplementary memberships. Running usermod -G alone can remove access to other groups.

    Verify the updated membership:

    groups alex

    You can also use:

    id alex

    Add a User to Multiple Groups

    Separate several group names with commas and no spaces:

    sudo usermod -aG developers,docker,projectteam alex

    Each listed group must already exist.

    Activate the New Membership

    Group changes may not affect an existing login session immediately. The user should log out and sign in again.

    For temporary access in the current terminal, run:

    newgrp developers

    A new shell will start with the selected group active.

    Create a User on Ubuntu and Debian

    Ubuntu and Debian provide the friendlier adduser utility:

    sudo adduser alex

    This interactive command creates the home directory, selects common defaults, asks for a password, and optionally collects account details.

    Create a group with:

    sudo addgroup developers

    Add the user to it:

    sudo adduser alex developers

    Both adduser and useradd can create accounts, but adduser guides beginners through the process while useradd provides direct control through options.

    Grant Administrative Privileges

    Grant Administrative Privileges

    Administrative access should only be given when necessary.

    On Ubuntu and Debian, add the user to the sudo group:

    sudo usermod -aG sudo alex

    On Fedora, Rocky Linux, AlmaLinux, and similar systems, use the wheel group:

    sudo usermod -aG wheel alex

    After signing in again, test administrative access:

    sudo whoami

    A successful command should return root.

    Use a Group for a Shared Directory

    Groups become especially useful when several people need to work in the same directory.

    Create the shared folder:

    sudo mkdir -p /srv/project

    Assign its group ownership:

    sudo chown :developers /srv/project

    Set collaborative permissions:

    sudo chmod 2775 /srv/project

    The leading 2 enables the setgid permission. New files and subdirectories created inside the folder inherit the developers group, making collaboration more consistent.

    Change a User’s Primary Group

    To change the primary group of an existing account, run:

    sudo usermod -g developers alex

    Confirm the change:

    id alex

    Changing the primary group does not automatically update ownership of older files. Review and adjust existing files when required.

    Delete Users and Groups Safely

    Delete Users and Groups Safely

    Delete a user while keeping the home directory:

    sudo userdel alex

    Delete the account and its home directory:

    sudo userdel -r alex

    Before using -r, back up any important files.

    Delete an unused group with:

    sudo groupdel developers

    Linux will refuse to remove a group if it is still configured as a user’s primary group.

    Troubleshoot Common Problems

    The User Already Exists

    If Linux reports that the user already exists, verify the account:

    getent passwd alex

    Choose another username or modify the existing account.

    The Group Does Not Exist

    Create the group before assigning it:

    sudo groupadd developers

    Then repeat the useradd or usermod command.

    Permission Is Denied

    User and group management requires root privileges. Add sudo before the command or sign in through an authorized administrative account.

    Group Access Is Still Not Working

    Ask the user to log out and sign in again. Also verify the folder’s ownership and permissions:

    ls -ld /srv/project

    id alex

    Group membership alone does not grant access when directory permissions block the group.

    Frequently Asked Questions

    1. How to Create Users and Groups in Linux at the Same Time?

    Create the group first with groupadd, then create the user with useradd -m -g groupname username, and finally set the password using passwd.

    2. How Do I See Every Group a User Belongs To?

    Run groups username for a simple list or id username for UID, GID, and complete membership details.

    3. What Is the Difference Between -g and -G?

    The lowercase -g sets one primary group, while uppercase -G assigns one or more supplementary groups.

    4. Does useradd Automatically Create a Home Directory?

    Not on every system. Use the -m option to ensure the account receives a home directory.

    Wrapping Up Your Linux Account Setup

    When I manage Linux accounts, I follow a repeatable process: check existing records, create the required group, create the user with a home directory, assign the correct memberships, set a secure password, and verify every change.

    Learning How to Create Users and Groups in Linux also makes file permissions, shared directories, application access, and server security easier to understand. By using groupadd, useradd, usermod, passwd, id, and getent carefully, I can maintain organized accounts without manually editing sensitive system files.

  • How to Monitor Servers With Prometheus: Complete Guide

    How to Monitor Servers With Prometheus: Complete Guide

    When I began exploring infrastructure monitoring, I wanted more than a dashboard full of numbers. I wanted to know when a server was overloaded, when storage was running low, and when a machine disappeared before users noticed a problem. Learning How to Monitor Servers With Prometheus provides exactly that visibility when Prometheus is combined with Node Exporter, PromQL, Grafana, and sensible alerting.

    This guide walks through the complete monitoring process, from collecting Linux server metrics to building dashboards, tracking multiple machines, securing monitoring endpoints, and troubleshooting common problems.

    How Prometheus Server Monitoring Works

    Prometheus uses a pull-based monitoring model. Instead of servers continuously sending monitoring data somewhere, Prometheus periodically connects to configured endpoints and collects metrics.

    For Linux server monitoring, Node Exporter normally provides those metrics.

    The basic architecture looks like this:

    Server → Node Exporter → Prometheus → Grafana → Alerts

    Node Exporter exposes operating-system and hardware information through an HTTP metrics endpoint. Prometheus scrapes that endpoint, stores the resulting time-series data, and lets administrators analyze it using PromQL.

    Grafana can then transform those measurements into dashboards, while alerting rules can notify administrators when important thresholds are reached.

    What Node Exporter Monitors

    Node Exporter provides metrics covering areas such as CPU activity, memory availability, system load, filesystem capacity, disk activity, network traffic and operating-system statistics.

    It commonly listens on port 9100, while the Prometheus web interface normally uses port 9090.

    Step 1: Install Prometheus

    Step 1 - Install Prometheus

    Prometheus should be installed on the machine responsible for collecting and storing monitoring data.

    After installation, verify that the Prometheus service is running and open its web interface.

    Depending on the operating system and deployment method, Prometheus may be installed using packages, containers or downloaded binaries.

    Before continuing, check the configuration by running promtool check config /etc/prometheus/prometheus.yml.

    A valid configuration helps prevent simple syntax mistakes from stopping metric collection.

    Step 2: Install Node Exporter

    Install Node Exporter on every Linux machine that Prometheus needs to monitor.

    Once the service is running, verify its status by running systemctl status node_exporter.

    Then confirm that metrics are available by entering curl http://localhost:9100/metrics.

    A successful response should contain numerous metrics beginning with names such as node_cpu, node_memory, node_filesystem and node_network.

    This test is useful because it proves that Node Exporter is functioning before Prometheus is introduced into the troubleshooting process.

    Step 3: Add the Server to Prometheus

    Step 3 - Add the Server to Prometheus

    Prometheus discovers servers through scrape configurations in the prometheus.yml file.

    A simple configuration uses scrape_configs with a job named linux_servers. Inside static_configs, add server1:9100 to the list of targets.

    After changing the file, validate the configuration and reload or restart Prometheus.

    Open Prometheus and check the target status. The server should appear as UP.

    If the target displays DOWN, investigate connectivity, firewall rules, hostname resolution and whether Node Exporter is actually listening on port 9100.

    Step 4: Monitor CPU Usage With PromQL

    Collecting metrics is only useful when they can answer practical questions.

    PromQL allows Prometheus data to be transformed into meaningful measurements.

    For example, CPU metrics can reveal how much processor time is being spent idle, handling system processes or executing applications.

    Rather than watching one instantaneous value, use rate-based queries over several minutes. This reduces noise and makes sustained CPU pressure easier to identify.

    High CPU alone does not always indicate failure. Compare CPU activity with system load, application response time and other metrics before deciding that a server has a problem.

    Step 5: Monitor Memory and Swap

    Step 5 - Monitor Memory and Swap

    Memory monitoring should focus on available memory rather than treating all cached memory as unavailable.

    Useful Node Exporter metrics include information about total memory, available memory and swap activity.

    Watch for patterns where available memory steadily declines or swap usage increases continuously. Those conditions can point to memory leaks, undersized servers or unusually demanding workloads.

    Temporary memory spikes may be harmless, so alerts should generally avoid triggering from a single brief change.

    Step 6: Monitor Disk Space and Disk I/O

    Running out of disk space can interrupt applications, databases, logging systems and operating-system services, including automated processes used to create CI/CD pipeline workflows.

    Monitor available filesystem capacity and calculate disk usage as a percentage.

    Pay particular attention to important filesystems rather than blindly alerting on every mounted device. Temporary and virtual filesystems can otherwise produce unnecessary noise.

    Disk I/O also deserves attention. A server may have plenty of free storage while suffering from slow reads, heavy writes or storage latency.

    Step 7: Monitor Network Traffic

    Step 7 - Monitor Network Traffic

    Node Exporter records network counters that Prometheus can convert into traffic rates.

    Monitoring received and transmitted bytes helps identify sudden traffic increases, unexpected drops or unusual bandwidth patterns.

    Network measurements become especially valuable when compared with application traffic. A large unexplained increase may indicate a deployment change, unexpected user activity or a malfunctioning service.

    How to Monitor Multiple Servers

    Prometheus can monitor many machines from one configuration. For example, under static_configs, add server1:9100, server2:9100 and server3:9100 to the list of targets.

    Prometheus automatically associates each target with labels such as instance.

    Those labels make it possible to compare CPU, memory, disk and network activity across individual servers while using the same PromQL expressions.

    For larger environments, service discovery is usually more manageable than maintaining long static target lists manually.

    Visualize Prometheus Metrics With Grafana

    Prometheus includes an expression browser, but Grafana provides a more practical interface for ongoing monitoring.

    Add Prometheus as a Grafana data source and create panels for important measurements such as CPU utilization, available memory, filesystem capacity, disk activity and network throughput.

    Existing Node Exporter dashboard templates can provide a useful starting point, although dashboards should eventually be adjusted around the services and infrastructure that actually matter.

    Avoid filling dashboards with every available metric. A smaller collection of actionable indicators usually provides greater operational value.

    Configure Prometheus Alerts

    Dashboards require someone to look at them. Alerts actively report important conditions.

    Useful alerting scenarios include:

    Server Down

    Trigger an alert when Prometheus cannot scrape a target for a sustained period.

    High CPU Usage

    Alert when CPU utilization remains unusually high instead of reacting to brief processing spikes.

    Low Available Memory

    Notify administrators when memory availability remains below a meaningful threshold.

    Low Disk Space

    Create filesystem alerts before capacity reaches a critical level so administrators have time to respond.

    Alert thresholds should reflect normal workloads. Poorly chosen rules create alert fatigue and make genuinely important warnings easier to ignore.

    Secure Prometheus and Node Exporter

    Secure Prometheus and Node Exporter

    Monitoring endpoints expose valuable infrastructure information and should not automatically be accessible from the public internet.

    Use firewall rules or private networking so that Node Exporter accepts connections only from trusted monitoring systems whenever possible.

    Also consider authentication, TLS, reverse proxies and restricted administrative access when Prometheus or Grafana must be accessible beyond a protected internal network.

    Security should be part of the initial monitoring architecture rather than something added after deployment.

    Troubleshooting Common Prometheus Problems

    Prometheus Target Shows DOWN

    Confirm that Node Exporter is running, port 9100 is reachable and the configured hostname or IP address is correct.

    Connection Refused on Port 9100

    Check whether Node Exporter is listening and whether firewall policies permit traffic from the Prometheus machine.

    Metrics Are Not Updating

    Review the scrape interval, target status, Prometheus logs and configuration syntax.

    Node Exporter Will Not Start

    Inspect its service logs and verify that another process is not already using the configured port.

    Following the monitoring path in order—Node Exporter, network connectivity, Prometheus target status, then PromQL—usually makes troubleshooting considerably faster.

    Frequently Asked Questions

    1. What is the easiest way to learn How to Monitor Servers With Prometheus?

    Start with one Linux machine running Node Exporter. Add it as a Prometheus target, verify that the target is UP, experiment with a few CPU and memory queries, and then add Grafana and alerts once metric collection is working.

    2. Does Prometheus need Node Exporter?

    Prometheus does not require Node Exporter for every monitoring task, but Node Exporter is one of the standard choices for collecting Linux host and operating-system metrics.

    3. Can Prometheus monitor several servers?

    Yes. Multiple servers can be added as scrape targets, while labels allow queries and dashboards to distinguish one machine from another.

    4. Is Grafana required for Prometheus?

    No. Prometheus provides its own query interface. Grafana is commonly added because it offers more flexible dashboards and visualization features.

    Final Thoughts

    When I build server monitoring, I prefer to start with a small number of metrics that answer real operational questions instead of collecting thousands of measurements without a purpose. Prometheus becomes much more valuable when Node Exporter metrics are connected to useful PromQL queries, clear dashboards and alerts that require action.

    A reliable setup should ultimately tell me whether servers are reachable, whether resources are becoming constrained, and where I should investigate when something changes. Adding secure access, multiple-server visibility and thoughtful alert thresholds turns a basic Prometheus installation into a monitoring system that can support real infrastructure.

  • How to Configure Nginx Reverse Proxy Step by Step

    How to Configure Nginx Reverse Proxy Step by Step

    When I first started working with web servers, reverse proxies sounded far more complicated than they actually were. Once I understood that Nginx simply receives a visitor’s request and forwards it to the correct backend application, the entire setup became much easier to manage. 

    Learning How to Configure Nginx Reverse Proxy can help you run applications behind a single domain, protect backend ports, manage HTTPS, and create a cleaner server architecture.

    What Is an Nginx Reverse Proxy?

    An Nginx reverse proxy sits between users and your backend application. Instead of visitors connecting directly to an application running on a port such as 3000, 8000, or 8080, they connect to Nginx through the normal HTTP or HTTPS ports.

    Nginx then forwards the request to the appropriate backend server and returns the application’s response to the visitor.

    This architecture is commonly used with Node.js, Python, PHP, Java, Docker containers, APIs, and other web applications.

    Why Use Nginx as a Reverse Proxy?

    A reverse proxy makes server management easier because users do not need to know where an application is actually running.

    Nginx can also terminate SSL connections, forward traffic to multiple applications, manage request headers, compress responses, cache certain content, apply rate limits, and distribute requests between multiple backend servers.

    Another important advantage is security. Your application can listen only on localhost while Nginx remains the public-facing server. This prevents visitors from directly accessing backend application ports.

    Prerequisites Before Configuration

    Prerequisites Before Configuration

    Before configuring the proxy, make sure Nginx is installed and your backend application is already running.

    You should also know the backend address and port. For example, an application might be available internally at http://127.0.0.1:3000.

    If you plan to enable HTTPS, you will also need a domain pointing to your server.

    How to Configure Nginx Reverse Proxy

    The basic setup requires creating a server block, defining the domain, forwarding requests to the backend, and passing useful request headers.

    Step 1: Install Nginx

    On Ubuntu or Debian-based systems, update the package list by running sudo apt update, and then install Nginx with sudo apt install nginx.

    Check whether Nginx is running by entering sudo systemctl status nginx.

    You should also confirm that your backend application responds correctly before introducing the proxy.

    Step 2: Create a Server Block

    Create a configuration file for your website inside the /etc/nginx/sites-available/ directory.

    A simple configuration begins with a server block that listens on port 80. Set server_name to example.com and www.example.com. Inside the server block, create a location / block and set proxy_pass to http://127.0.0.1:3000.

    Here, server_name identifies the domain, while location / tells Nginx that requests beginning at the root path should be handled by this block.

    The proxy_pass directive determines where Nginx sends those requests.

    Step 3: Add Forwarded Headers

    A more production-friendly location / block should set proxy_pass to http://127.0.0.1:3000 and include the following forwarded-header settings:

    Set the Host header to $host. Set X-Real-IP to $remote_addr. Set X-Forwarded-For to $proxy_add_x_forwarded_for. Finally, set X-Forwarded-Proto to $scheme.

    These headers help backend applications identify the requested host, client IP address, proxy chain and original protocol. When you configure a reverse proxy, it is also important to secure SSH on Ubuntu server to protect administrative access and reduce the risk of unauthorised changes.

    Without them, applications may produce incorrect redirects, misleading logs or unreliable HTTPS detection.

    Step 4: Enable the Configuration

    Create a symbolic link inside sites-enabled by running sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/.

    Always test the configuration before reloading Nginx. Run sudo nginx -t to check it.

    If the test succeeds, reload the service by entering sudo systemctl reload nginx.

    Your domain should now forward requests to the backend application.

    Understanding proxy_pass and Trailing Slashes

    Understanding proxy_pass and Trailing Slashes

    One detail that frequently causes confusion in a reverse proxy setup is the URI used with proxy_pass.

    For example, a location /api/ block containing proxy_pass http://127.0.0.1:3000/; is not always equivalent to a location /api/ block containing proxy_pass http://127.0.0.1:3000;.

    Adding or removing the trailing slash can affect how Nginx rewrites the request URI before forwarding it.

    When applications rely on specific paths, test the exact URL reaching the backend rather than assuming both configurations behave identically.

    Add HTTPS to the Reverse Proxy

    Production websites should normally use HTTPS.

    A common approach is obtaining a TLS certificate through Let’s Encrypt and configuring Nginx to listen on port 443.

    After HTTPS is active, Nginx can handle encrypted connections while communicating with an internal backend over HTTP when appropriate.

    Remember to set the X-Forwarded-Proto header to $scheme. This allows your application to recognise that the original visitor connected securely.

    Configure WebSockets Through Nginx

    Applications using WebSockets often require additional settings. Set proxy_http_version to 1.1, set the Upgrade header to $http_upgrade and set the Connection header to “upgrade.”

    Without these settings, ordinary HTTP requests may work while live chat, dashboards, notifications or other real-time features fail.

    Common Nginx Reverse Proxy Errors

    Common Nginx Reverse Proxy Errors

    502 Bad Gateway

    A 502 error usually means Nginx cannot successfully communicate with the backend.

    Check whether the application is running, verify the port number, confirm the application is listening on the expected interface, and inspect the Nginx and application logs.

    Also, test the backend directly from the server.

    Too Many Redirects

    Redirect loops frequently occur when the backend does not correctly recognise HTTPS.

    Forwarding the X-Forwarded-Proto header and configuring the application to trust proxy headers can solve this issue.

    Incorrect Client IP Address

    If application logs show the proxy address instead of the visitor’s real IP address, verify the X-Real-IP and X-Forwarded-For settings.

    WebSocket Connection Failures

    If normal pages work but real-time features do not, check the Upgrade and Connection headers and ensure that HTTP/1.1 proxying is enabled.

    Nginx Reverse Proxy Best Practices

    Once you understand How to Configure Nginx Reverse Proxy, the next goal should be making the setup reliable rather than simply making it work.

    Keep backend services inaccessible from the public internet whenever possible. Use HTTPS for public traffic, test every configuration change with the nginx -t command, maintain useful access and error logs, and avoid excessively generous timeout settings unless your application genuinely requires them.

    You can also use Nginx for rate limiting, caching, compression, load balancing, and security headers when your application architecture requires additional protection or performance improvements.

    Frequently Asked Questions

    1. What Does Proxy Pass Do in Nginx?

    The proxy_pass directive defines the backend server or application that should receive requests matching a particular Nginx location block.

    2. How Do I Fix a 502 Bad Gateway Error?

    Verify that your backend is running, confirm its IP address and port, check firewall rules, and inspect both Nginx and application error logs.

    3. Can Nginx Proxy Multiple Applications?

    Yes. Different domains, subdomains, or URL paths can use separate server or location blocks that send requests to different backend services.

    4. Is How to Configure Nginx Reverse Proxy Difficult for Beginners?

    No. The basic setup requires only a server block, the proxy_pass directive, forwarded headers, configuration testing, and a reload. HTTPS and WebSockets can then be added as needed.

    Final Takeaways

    I find Nginx reverse proxy setups easiest to manage when they are built gradually. I start with a small working configuration, verify that Nginx can communicate with the backend, add the required headers, and only then introduce HTTPS, WebSockets, caching, or other production features.

    That approach also makes troubleshooting much faster. Instead of dealing with a large configuration full of unknowns, I can isolate each layer and confirm that it works before moving forward. A well-configured reverse proxy ultimately gives you a cleaner public interface, safer backend services, and far more flexibility as your application infrastructure grows.