Cloud Explainers

How to Fix nginx 502 Bad Gateway Errors on Linux

Linux7 min videoPublished Nov 13, 2026

Your site shows 502 Bad Gateway and nginx says it's running. That's because a 502 is nginx telling you the app behind it gave no usable answer. In a real lab we broke a Python app behind nginx four ways and read nginx's error log each time: the words in that one line name the cause, and each has a fix you can check with nginx -t and curl.

nginx is running. The page says 502

Your website shows 502 Bad Gateway, and nginx says it's running. This is Ana's climbing gym booking page. An hour ago it listed the week's sessions. Now it says this, signed by nginx.

nginx is running. The page says 502: browser screenshot of an hour ago, the error page, made by nginx itself

curl, which fetches a page from the command line, asks for just its status: 502. systemctl, the tool that manages services, says nginx is active and running. Most people restart it anyway. Guess first: does that help? No. Still 502.

ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 502 Bad Gateway
ana@gym: Ana's terminal
systemctl status nginx --no-pager● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-11-13 17:43:16 UTC; 36s ago
       Docs: man:nginx(8)
    Process: 179 ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload (code=exited, status=0/SUCCESS)
   Main PID: 95 (nginx)
      Tasks: 11 (limit: 9563)
     Memory: 7.8M (peak: 15.2M)
        CPU: 31ms
     CGroup: /system.slice/nginx.service
             ├─ 95 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─181 "nginx: worker process"
             ├─182 "nginx: worker process"
             ├─183 "nginx: worker process"
             ├─184 "nginx: worker process"
             ├─185 "nginx: worker process"
             ├─186 "nginx: worker process"
             ├─187 "nginx: worker process"
             ├─188 "nginx: worker process"
             ├─189 "nginx: worker process"
             └─190 "nginx: worker process"

Nov 13 17:43:16 gym systemd[1]: Starting nginx.service - A high performance web server an…ver...
Nov 13 17:43:16 gym systemd[1]: Started nginx.service - A high performance web server and…erver.
Nov 13 17:43:17 gym systemd[1]: Reloading nginx.service - A high performance web server a…ver...
Nov 13 17:43:17 gym nginx[179]: 2026/11/13 17:43:17 [notice] 179#179: signal process started
Nov 13 17:43:17 gym systemd[1]: Reloaded nginx.service - A high performance web server an…erver.
Hint: Some lines were ellipsized, use -l to show in full.
ana@gym: Ana's terminal
sudo systemctl restart nginx
ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 502 Bad Gateway

What a bad gateway is

Here's why. nginx isn't the website. It passes each request to the app behind it, here a small Python app run by gunicorn, which nginx calls the upstream. nginx is the waiter, the app is the kitchen. A 502 is the waiter coming back with: the kitchen gave me nothing I can serve. Restarting the waiter changes nothing. We'll find four causes in the kitchen, including one where everything is running and nginx still isn't let in.

nginx passes each request to the app behind it: diagram of Browser, nginx, The app = upstream, Ana's server

The quick fix: nginx's error log

So the quick fix is to stop guessing and ask nginx what the kitchen said. nginx writes it in its error log. This command prints the last line. Connect failed: Connection refused. Refused means nothing is listening at that address. And the address is at the end: the upstream, 127.0.0.1, port 8000.

ana@gym: Ana's terminal
sudo tail -n 1 /var/log/nginx/error.log2026/11/13 17:43:53 [error] 287#287: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: _, request: "HEAD / HTTP/1.1", upstream: "http://127.0.0.1:8000/", host: "localhost"

Now skip the waiter and knock on the kitchen yourself: curl straight to that address. Failed to connect. So the app is the problem, not nginx. Its service says failed. And its own log, read with journalctl, says why: a key error. Ana's new version needs a setting called BOOKING_DB, and the service file doesn't give it one. So the app crashed at start, and systemd gave up after a few tries.

ana@gym: Ana's terminal
curl -I 127.0.0.1:8000curl: (7) Failed to connect to 127.0.0.1 port 8000 after 0 ms: Couldn't connect to server
ana@gym: Ana's terminal
systemctl status booking --no-pager× booking.service - Crux Climbing booking app (gunicorn)
     Loaded: loaded (/etc/systemd/system/booking.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Fri 2026-11-13 17:43:50 UTC; 2s ago
   Duration: 80ms
    Process: 243 ExecStart=/usr/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 booking:app (code=exited, status=3)
   Main PID: 243 (code=exited, status=3)
        CPU: 79ms

Nov 13 17:43:50 gym systemd[1]: booking.service: Scheduled restart job, restart counter is at 5.
Nov 13 17:43:50 gym systemd[1]: booking.service: Start request repeated too quickly.
Nov 13 17:43:50 gym systemd[1]: booking.service: Failed with result 'exit-code'.
Nov 13 17:43:50 gym systemd[1]: Failed to start booking.service - Crux Climbing booking a…corn).
Hint: Some lines were ellipsized, use -l to show in full.
ana@gym: Ana's terminal
journalctl -u booking -n 40 --no-pagerNov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3/dist-packages/gunicorn/workers/base.py", line 134, in init_process
Nov 13 17:43:50 gym gunicorn[244]:     self.load_wsgi()
Nov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3/dist-packages/gunicorn/workers/base.py", line 146, in load_wsgi
Nov 13 17:43:50 gym gunicorn[244]:     self.wsgi = self.app.wsgi()
Nov 13 17:43:50 gym gunicorn[244]:                 ^^^^^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3/dist-packages/gunicorn/app/base.py", line 67, in wsgi
Nov 13 17:43:50 gym gunicorn[244]:     self.callable = self.load()
Nov 13 17:43:50 gym gunicorn[244]:                     ^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3/dist-packages/gunicorn/app/wsgiapp.py", line 58, in load
Nov 13 17:43:50 gym gunicorn[244]:     return self.load_wsgiapp()
Nov 13 17:43:50 gym gunicorn[244]:            ^^^^^^^^^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3/dist-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
Nov 13 17:43:50 gym gunicorn[244]:     return util.import_app(self.app_uri)
Nov 13 17:43:50 gym gunicorn[244]:            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3/dist-packages/gunicorn/util.py", line 384, in import_app
Nov 13 17:43:50 gym gunicorn[244]:     mod = importlib.import_module(module)
Nov 13 17:43:50 gym gunicorn[244]:           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "/usr/lib/python3.12/importlib/__init__.py", line 90, in import_module
Nov 13 17:43:50 gym gunicorn[244]:     return _bootstrap._gcd_import(name[level:], package, level)
Nov 13 17:43:50 gym gunicorn[244]:            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen importlib._bootstrap_external>", line 995, in exec_module
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
Nov 13 17:43:50 gym gunicorn[244]:   File "/home/ana/booking/booking.py", line 13, in <module>
Nov 13 17:43:50 gym gunicorn[244]:     DB = Path(os.environ["BOOKING_DB"])          # new in this version: where the sessions file lives
Nov 13 17:43:50 gym gunicorn[244]:               ~~~~~~~~~~^^^^^^^^^^^^^^
Nov 13 17:43:50 gym gunicorn[244]:   File "<frozen os>", line 685, in __getitem__
Nov 13 17:43:50 gym gunicorn[244]: KeyError: 'BOOKING_DB'
Nov 13 17:43:50 gym gunicorn[244]: [2026-11-13 17:43:50 +0000] [244] [INFO] Worker exiting (pid: 244)
Nov 13 17:43:50 gym gunicorn[243]: [2026-11-13 17:43:50 +0000] [243] [INFO] Shutting down: Master
Nov 13 17:43:50 gym gunicorn[243]: [2026-11-13 17:43:50 +0000] [243] [INFO] Reason: Worker failed to boot.
Nov 13 17:43:50 gym systemd[1]: booking.service: Main process exited, code=exited, status=3/NOTIMPLEMENTED
Nov 13 17:43:50 gym systemd[1]: booking.service: Failed with result 'exit-code'.
Nov 13 17:43:50 gym systemd[1]: booking.service: Scheduled restart job, restart counter is at 5.
Nov 13 17:43:50 gym systemd[1]: booking.service: Start request repeated too quickly.
Nov 13 17:43:50 gym systemd[1]: booking.service: Failed with result 'exit-code'.
Nov 13 17:43:50 gym systemd[1]: Failed to start booking.service - Crux Climbing booking app (gunicorn).

In her editor, Ana adds the setting to the service file, and grep shows the line. daemon-reload makes systemd read the changed file, then she restarts the app, not nginx. And the page answers 200 OK.

ana@gym: Ana's terminal
grep -n BOOKING_DB /etc/systemd/system/booking.service9:Environment=BOOKING_DB=/home/ana/booking/sessions.csv
ana@gym: Ana's terminal
sudo systemctl daemon-reload
ana@gym: Ana's terminal
sudo systemctl restart booking
ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 200 OK

Refused, but the app is running

Same error, different day. 502, and the log says Connection refused again, at port 8000. But this time the app is active. So where is it? The ss command lists the ports programs are listening on, and grep keeps gunicorn's line. gunicorn is on port 8080. Ana's teammate moved the app, and nobody told nginx. The waiter is knocking on the old kitchen door.

ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 502 Bad Gateway
ana@gym: Ana's terminal
sudo tail -n 1 /var/log/nginx/error.log2026/11/13 17:44:09 [error] 288#288: *5 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: _, request: "HEAD / HTTP/1.1", upstream: "http://127.0.0.1:8000/", host: "localhost"
ana@gym: Ana's terminal
systemctl is-active bookingactive
ana@gym: Ana's terminal
sudo ss -ltnp | grep gunicornLISTEN 0      2048       127.0.0.1:8080      0.0.0.0:*    users:(("gunicorn",pid=381,fd=5),("gunicorn",pid=380,fd=5),("gunicorn",pid=372,fd=5))

The address lives on nginx's proxy pass line. Ana changes 8000 to 8080, and before anything else, nginx -t tests the whole configuration. Test is successful. Then a reload, which keeps nginx up while it reads the change. 200 OK.

ana@gym: Ana's terminal
grep -n proxy_pass /etc/nginx/sites-available/booking6:        proxy_pass http://127.0.0.1:8000;
ana@gym: Ana's terminal
grep -n proxy_pass /etc/nginx/sites-available/booking6:        proxy_pass http://127.0.0.1:8080;
ana@gym: Ana's terminal
sudo nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
ana@gym: Ana's terminal
sudo systemctl reload nginx
ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 200 OK

A slow page: 502 or 504?

Now a page that's slow, not broken. The monthly report takes seventy-five seconds to build. curl times the request. A gateway timeout is error 504. So guess first: what does the visitor get? A 502, after thirty seconds. The log says the upstream closed the connection early, and the app's log says why: worker timeout.

ana@gym: Ana's terminal
curl -s -o /dev/null -w '%{http_code} after %{time_total}s\n' localhost/report502 after 30.666800s
ana@gym: Ana's terminal
sudo tail -n 1 /var/log/nginx/error.log2026/11/13 17:44:42 [error] 411#411: *9 upstream prematurely closed connection while reading response header from upstream, client: 127.0.0.1, server: _, request: "GET /report HTTP/1.1", upstream: "http://127.0.0.1:8080/report", host: "localhost"
ana@gym: Ana's terminal
journalctl -u booking -n 3 --no-pagerNov 13 17:44:08 gym gunicorn[381]: [2026-11-13 17:44:08 +0000] [381] [INFO] Booting worker with pid: 381
Nov 13 17:44:42 gym gunicorn[372]: [2026-11-13 17:44:42 +0000] [372] [CRITICAL] WORKER TIMEOUT (pid:380)
Nov 13 17:44:42 gym gunicorn[380]: [2026-11-13 17:44:42 +0000] [380] [INFO] Worker exiting (pid: 380)

That's gunicorn's own timer: it stops any worker that's silent for thirty seconds. So Ana raises it to a hundred and twenty, in the service file, and restarts the app. Now nginx's own default limit runs out first: sixty seconds, and the real 504. A different log line too: upstream timed out. Closed early means 502. Timed out means 504.

ana@gym: Ana's terminal
grep -n timeout /etc/systemd/system/booking.service10:ExecStart=/usr/bin/gunicorn --workers 2 --timeout 120 --bind 127.0.0.1:8080 booking:app
ana@gym: Ana's terminal
sudo systemctl daemon-reload
ana@gym: Ana's terminal
sudo systemctl restart booking
ana@gym: Ana's terminal
curl -s -o /dev/null -w '%{http_code} after %{time_total}s\n' localhost/report504 after 60.066385s
ana@gym: Ana's terminal
sudo tail -n 1 /var/log/nginx/error.log2026/11/13 17:45:44 [error] 412#412: *11 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: _, request: "GET /report HTTP/1.1", upstream: "http://127.0.0.1:8080/report", host: "localhost"

nginx's limit is the proxy read timeout. Ana sets it to a hundred and twenty seconds, tests, and reloads. 200, after seventy-five.

ana@gym: Ana's terminal
grep -n proxy_read_timeout /etc/nginx/sites-available/booking9:        proxy_read_timeout 120s;
ana@gym: Ana's terminal
sudo nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
ana@gym: Ana's terminal
sudo systemctl reload nginx
ana@gym: Ana's terminal
curl -s -o /dev/null -w '%{http_code} after %{time_total}s\n' localhost/report200 after 75.005155s

Three runs of the same report: whichever timer runs out first decides the error. It works now, but every visitor still waits seventy-five seconds. The lasting fix is a faster page, or building the report in the background.

The same 75-second report, three times: chart of 502 gunicorn stops it: 30.7 s, 504 nginx gives up: 60.1 s, 200 both raised to 120 s: 75 s, Better: make the page fast

An answer too big for nginx

This one only hits the sign-in page. 502, every time, with the app running fine. The log: upstream sent too big header. The header is the part of the app's answer before the page itself, and nginx reads it into one small buffer: one memory page, here four thousand ninety-six bytes. Ana's sign-in cookie made the header five thousand sixty-three bytes. The kitchen sent a plate too big for the waiter's tray.

ana@gym: Ana's terminal
curl -s -o /dev/null -w '%{http_code}\n' localhost/login502
ana@gym: Ana's terminal
sudo tail -n 1 /var/log/nginx/error.log2026/11/13 17:48:01 [error] 484#484: *17 upstream sent too big header while reading response header from upstream, client: 127.0.0.1, server: _, request: "GET /login HTTP/1.1", upstream: "http://127.0.0.1:8080/login", host: "localhost"
ana@gym: Ana's terminal
curl -s -D - -o /dev/null 127.0.0.1:8080/login | wc -c5063
ana@gym: Ana's terminal
getconf PAGESIZE4096

The setting is proxy buffer size. Ana sets sixteen kilobytes, and tests first. Refused. nginx's other buffers are still four kilobytes each, too small to go with it. Notice the site is still up: a failed test changes nothing live. She raises the other buffers, eight of them, to sixteen kilobytes each, tests, reloads, and sign-in returns 200. Smaller cookies are the lasting fix.

ana@gym: Ana's terminal
grep -n proxy_buffer /etc/nginx/sites-available/booking10:        proxy_buffer_size 16k;
ana@gym: Ana's terminal
sudo nginx -t2026/11/13 17:48:01 [emerg] 512#512: "proxy_busy_buffers_size" must be less than the size of all "proxy_buffers" minus one buffer in /etc/nginx/nginx.conf:61
nginx: configuration file /etc/nginx/nginx.conf test failed
ana@gym: Ana's terminal
grep -n proxy_buffer /etc/nginx/sites-available/booking10:        proxy_buffer_size 16k;
11:        proxy_buffers 8 16k;
ana@gym: Ana's terminal
sudo nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
ana@gym: Ana's terminal
sudo systemctl reload nginx
ana@gym: Ana's terminal
curl -s -o /dev/null -w '%{http_code}\n' localhost/login200

The socket in a home folder

The last one is the case where everything runs and nginx still isn't let in. Ana followed a guide that connects nginx and gunicorn through a socket: a special file the two programs talk through, here in her home folder. 502, and the log says: Permission denied. The socket itself is open to everyone. But nginx's workers run as a user called www-data, and to reach the socket they must pass through Ana's home folder. In its permissions, the last three characters are for everyone else, and dashes mean: keep out. Ubuntu makes new home folders that way.

ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 502 Bad Gateway
ana@gym: Ana's terminal
sudo tail -n 1 /var/log/nginx/error.log2026/11/13 17:48:05 [crit] 585#585: *23 connect() to unix:/home/ana/booking/booking.sock failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: _, request: "HEAD / HTTP/1.1", upstream: "http://unix:/home/ana/booking/booking.sock:/", host: "localhost"
ana@gym: Ana's terminal
ls -l /home/ana/booking/booking.socksrwxrwxrwx 1 ana ana 0 Nov 13 17:48 /home/ana/booking/booking.sock
ana@gym: Ana's terminal
ps -o user= -C nginx | sort | uniq -c      1 root
     10 www-data
ana@gym: Ana's terminal
ls -ld /home/anadrwxr-x--- 1 ana ana 4096 Nov 13 17:43 /home/ana

Don't open up your home folder. Move the socket. One line in the service file, runtime directory, makes systemd create a folder for it under /run, readable by nginx, and nginx points at the new path. Restart the app, reload nginx, and the page is back. Every command and the lab files are linked in the description.

ana@gym: Ana's terminal
grep -n -e Runtime -e bind /etc/systemd/system/booking.service10:RuntimeDirectory=booking
11:ExecStart=/usr/bin/gunicorn --workers 2 --timeout 120 --bind unix:/run/booking/booking.sock booking:app
ana@gym: Ana's terminal
grep -n proxy_pass /etc/nginx/sites-available/booking6:        proxy_pass http://unix:/run/booking/booking.sock;
ana@gym: Ana's terminal
sudo systemctl daemon-reload
ana@gym: Ana's terminal
sudo systemctl restart booking
ana@gym: Ana's terminal
sudo systemctl reload nginx
ana@gym: Ana's terminal
ls -ld /run/bookingdrwxr-xr-x 2 ana ana 60 Nov 13 17:48 /run/booking
ana@gym: Ana's terminal
curl -sI localhost | head -n 1HTTP/1.1 200 OK

Recap

So, a 502: read nginx's error log first. Refused: the app is down, or at another address. Closed early: the app's own timeout. Too big: the first buffer. Denied: the socket's folder. The waiter is fine. Check the kitchen.

A 502: read nginx's error log. Its words name the cause: diagram of nginx's error log, Connection refused, Prematurely closed, Too big header, Permission denied

If your site is back to 200, a like helps YouTube show this to the next person staring at a 502, and subscribing brings more real fixes like these.

Every error here came from a real nginx and a real app, in our lab. Thanks for watching Cloud Explainers.

Sources

Demos: a container on our own Mac standing in for a small web server; the climbing gym, its page and its app are made up. The restaurant is an analogy. Watch the full video on YouTube.

More videos

Networking11 min

Every Networking Term a Beginner Needs in 12 Minutes

Docker6 min

How to Fix Docker 'Permission Denied' on docker.sock

Git7 min

How to Fix Git 'Updates Were Rejected' When You Push

Linux8 min

How to Fix Linux apt 'Could Not Get Lock' Errors (Ubuntu)

Networking9 min

How Email Works on the Internet (SMTP) in 10 Minutes

Python8 min

The Python Roadmap for Beginners

Go (Golang)21 min

Golang REST API with Gin: Full Tutorial for Beginners

Networking10 min

How a WireGuard VPN Network Works

Linux9 min

Linux Makefiles Will Finally Make Sense After This

Auth (OAuth, JWT)9 min

SAML vs OAuth vs OpenID Connect: What's the Difference?

Python7 min

How to Fix Python cron Jobs That Never Run

Linux22 min

Linux Bash Scripting: Full Tutorial for Beginners

Networking10 min

How PXE Network Boot Works

Active Directory9 min

How Active Directory Works

Linux10 min

How to Fix a Linux systemd Service That Won't Start

Linux9 min

Linux Logs: syslog vs journald

Auth (OAuth, JWT)9 min

JWT vs Session Cookies: Which One Should You Use?

Docker10 min

Docker vs Docker Compose: Which One Should You Use?

Docker9 min

How to Fix Docker Compose Containers That Can't Talk to Each Other

Django (Python web framework)22 min

Django Full Tutorial for Beginners

Linux9 min

How Linux systemd Works

Networking8 min

How to Fix 'Windows Cannot Access' Network Share Errors (SMB)

APIs10 min

REST vs gRPC: Which One Should You Use?

Certificates & PKI (ADCS)9 min

How to Fix SSL Certificate Chain Errors (Missing Intermediate)

Python9 min

Every Python Concept for Beginners in 10 Minutes

Databases & SQL9 min

Every SQL Join Type in 10 Minutes

OpenRouter (one API for many AI models)9 min

Why OpenRouter Free Models Hit Their Limit So Fast

Gemini API & Google AI Studio9 min

Why Your Gemini API Free Tier Runs Out So Fast

OpenRouter (one API for many AI models)10 min

OpenRouter Will Finally Make Sense After This

Caching & Redis10 min

How Redis Caching Works

Gemini API & Google AI Studio10 min

Gemini API vs Vertex AI: Which One Should You Use?

Gemini API & Google AI Studio8 min

How to Fix a Leaked Gemini API Key

Go (Golang)9 min

Why Golang Uses Goroutines Instead of Threads

Gemini API & Google AI Studio9 min

How to Fix Gemini API Error 429: Resource Exhausted

Claude & the Claude Developer Platform10 min

Claude Token Pricing Will Finally Make Sense After This

Python11 min

Why Python Web Scraping Returns Nothing (and the Fix)

Python10 min

Python uv vs pip: Which One Should You Use?

Python22 min

Python Automation: Full Tutorial for Beginners

Auth (OAuth, JWT)9 min

How OAuth Login Works

Gemini API & Google AI Studio10 min

Google AI Studio Will Finally Make Sense After This

Docker10 min

What Happens When You Run a Docker Container

Claude & the Claude Developer Platform8 min

How to Fix Claude Code Billing Your API Key Instead of Your Plan

Auth (OAuth, JWT)9 min

Why Passkeys Can't Be Phished Like Passwords

PowerShell10 min

PowerShell vs Bash: Which One Should You Learn?

Linux10 min

The Linux Roadmap for Beginners

Django (Python web framework)9 min

Django vs FastAPI: Which One Should You Use?

Databases & SQL10 min

Why SQLite Is the Most Used Database in the World

Databases & SQL11 min

How a NoSQL Database Like MongoDB Works

Python11 min

How to Fix Python pip error: externally-managed-environment

Databases & SQL9 min

How to Fix MySQL Too Many Connections Database Errors

Go (Golang)9 min

Golang Concurrency Will Finally Make Sense After This

AWS10 min

How Amazon S3 Works

Kubernetes10 min

How Kubernetes Works

Git10 min

How Git Works

Docker10 min

How to Fix Docker Port Already Allocated Errors

PostgreSQL10 min

How PostgreSQL Works

Git8 min

How to Fix Git Merge Conflicts

Linux9 min

How to Fix No Space Left on Device in Linux

Networking10 min

How the Internet Works

Databases & SQL9 min

SQL vs NoSQL: When Each One Wins

Auth (OAuth, JWT)8 min

How to Fix JWT Token Expired Errors (401 Unauthorized)

Claude & the Claude Developer Platform9 min

Claude API vs Claude Pro: Why Your Subscription Doesn't Include It

AI agent skills9 min

Why AI Agent Skills Beat Giant Prompts

Linux10 min

Linux Cron Jobs Will Finally Make Sense After This

Cybersecurity10 min

Security Logs Will Finally Make Sense After This

Cybersecurity8 min

How to Fix a Security Leak: An API Key Pushed to GitHub

DNS9 min

DNS Will Finally Make Sense After This

Cybersecurity9 min

Never Ignore a Security Update

Auth (OAuth, JWT)9 min

Two-Factor Authentication Will Finally Make Sense

Networking8 min

Never Expose a Database to the Internet

Cybersecurity10 min

SSH Keys vs Passwords: Which Is Safer for Security?

Linux10 min

The 5 Levels of Linux Boot: From Power On to Login

Networking12 min

The 5 Levels of Network Firewalls

Cybersecurity10 min

Password Security: Why Hackers Crack Weak Passwords

Linux9 min

Why Your Linux Server Runs Out of Memory

Networking10 min

HTTP vs HTTPS: What Actually Changes on Your Network

Linux12 min

Every Linux File Permission You Need to Know

Linux10 min

Linux Processes vs Threads: When Each One Wins

Networking11 min

The OSI Model Will Finally Make Sense After This

Git11 min

The 7 Levels of Git

Networking10 min

Every Network Troubleshooting Command You Need

Cybersecurity10 min

Hashing vs Encryption in Cybersecurity: When Each One Wins

Networking11 min

Why the Internet Ran Out of IP Addresses

Linux11 min

Linux Package Managers: apt vs Snap vs Flatpak

Python9 min

Why Python Is Slow (and When It Doesn't Matter)

Networking11 min

TCP vs UDP: When Each One Wins

Linux8 min

Stop Running Linux as Root (Do This Instead)

Linux11 min

Linux Users and Groups Will Finally Make Sense

Cybersecurity9 min

Why Phishing Beats Good Security (and How to Spot It)

Docker12 min

The 7 Levels of Docker

AI agents & MCP12 min

AI Agents Will Finally Make Sense After This

Git8 min

Stop Using git push --force (Use This Instead)

AI agent skills10 min

Agent Skills vs MCP: When Each One Wins

Networking12 min

Networking Will Finally Make Sense After This

Linux12 min

Every Linux Command You Actually Need

Cybersecurity12 min

The 7 Levels of Cybersecurity

System design12 min

Every System Design Concept You Need, in One Diagram

PostgreSQL10 min

Why PostgreSQL Ignores Your Index