Elastic Beanstalk 502 Bad Gateway (nginx): The Port Fix

Logeshwaran
—

Elastic Beanstalk 502 Bad Gateway from nginx means the reverse proxy in front of your app could not get a usable answer from your application process. Nginx is alive and answering; the thing behind it is not listening where nginx is knocking, or it crashed, or it took too long. In most cases the fix is one number: make your app listen on the port the platform's nginx forwards to, through the PORT environment property and a root-level Procfile, then point the load balancer's health check at a fast path that returns 200. The surprise that causes half of these: 8080 is not the Elastic Beanstalk port. Node.js and Java SE platforms proxy to 5000, Python's WSGI server defaults to 8000, and Spring Boot's own default of 8080 is exactly why a JAR that runs perfectly on a laptop returns 502 the moment it is deployed. Copying a port fix from a different platform creates the very error you are trying to remove.

Jake runs a phone repair shop. His customer booking page lives on Elastic Beanstalk, where Ethan, his friend who does the technical side, put it last spring. It worked yesterday. This morning, after a routine deployment, customers get a gray page that says 502 Bad Gateway, and the Beanstalk console cheerfully shows the environment exists and the deployment finished.

Jake: "The console says the deployment succeeded. The website says 502. One of them is lying."

Ethan: "Neither. The deployment did succeed: your files landed on the server. 502 is the doorman saying he knocked on your app's door and nobody answered. Those are two different events."

Jake: "Doorman?"

Ethan: "Nginx. It is a program that sits in front of your app, takes every visitor's request and passes it to your app on a private door number, a port. The proxy is answering, which is how you got a 502 instead of nothing at all. Your app may not be answering behind it. That difference tells us where to look first."

⚡ Quick Answer

• Get the logs: Elastic Beanstalk → Environments → your environment → Logs → Request logs (full). Read /var/log/nginx/error.log, then /var/log/web.stdout.log, then /var/log/eb-engine.log. In that order, and why.

• Find the hop: connect() failed (111: Connection refused) in nginx = your app is not listening on the port nginx expects. The five-minute test.

• Fix the port: Node and Java SE → 5000; Python WSGI → 8000; Spring Boot → server.port=${PORT:5000}. The platform table.

• Start it right: a Procfile at the root of the zip, web: java -jar application.jar or web: node server.js. Procfile rules.

• Environment red too? Give the load balancer a /health path that returns 200 in milliseconds, without a login or a database query. Health checks.

Raising the load balancer timeout is the last fix, not the first. It cannot wake a process that never started.

If "Elastic Beanstalk", "environment" and "nginx" are new words, our plain-English page on what Elastic Beanstalk is takes five minutes and explains the platform this page is fixing. Jake had not read it. The doorman analogy got him through.

🧭 NEW HERE? READ THESE FIRST

New to Elastic Beanstalk? These five pages explain the pieces this error sits on:

📌 Bookmark this; the port table and the nginx message table are the two you will come back for.

What a 502 from nginx actually tells you

A visitor's request to Jake's booking page travels through three hops: the load balancer, which spreads traffic across servers; nginx on the server, the reverse proxy that accepts the request on the public port and passes it to the app on a private one; and the application process itself, Node, Python, Java or a container. A 502 means one hop asked the next hop for an answer and got nothing usable. It can come from nginx (the app did not answer nginx) or from the load balancer (nginx did not answer the load balancer). The first job is to find out which.

What a 502 is not: a reason to change DNS, restart everything, or open every port in the security group. The network rules that worked yesterday still work. Fixing the one broken hop is faster and leaves the correct settings alone.

🙋 Jake's Reality Check

Jake: "The environment health says green. Doesn't that mean the app works?"

Ethan: "It means the path the health check pokes answered. Your customers use a different path, at a different moment, possibly on a different server. Green and 502 can both be true at once, and today they are."

The first useful clue is one failing request with its exact time. Open the site, note the second it failed, then read the nginx and application log lines from that second. A narrow window beats a day of deployment output.

Find the failing hop in five minutes

Before you change a port or a Procfile, prove which hop is failing. The whole test is a short chain of requests on one affected server:

  1. Open Elastic Beanstalk → Environments → the failing environment. Write down the platform branch, the health, the recent events and the deployment version.
  2. Open Logs and request the full bundle, or read the streamed CloudWatch logs. Download the bundle before you deploy anything else; a redeploy can overwrite the evidence.
  3. In /var/log/nginx/error.log, look for connect() failed (111: Connection refused) while connecting to upstream, upstream prematurely closed connection or upstream timed out.
  4. In the application log, find the line announcing which port the app is listening on, or the exception it threw while starting.
  5. If you can get a shell on the instance, run ss -ltnp to list what is listening, then curl -i http://127.0.0.1:PORT/health against the port your app claims.
  6. Compare three numbers: the port the app binds, the port in nginx's proxy_pass, and the PORT environment property.
eb status
eb events
eb logs --all

# on an affected instance
sudo ss -ltnp
curl -i http://127.0.0.1:5000/health
sudo nginx -T | grep -n -A5 -B5 proxy_pass

Treat the 5000 in that curl as a placeholder for whatever your platform expects. Three outcomes, three directions. Connection refused on localhost: the process is not running or not listening; go to startup and ports. Localhost works, nginx still 502s: nginx is pointing at a different port than the app uses; compare proxy_pass with ss. Nginx works directly, the load balancer still fails: listeners, target group, health check, timeouts.

Jake: "Three numbers that have to match. That's it?"

Ethan: "Pick one port for each hop, write it down, and compare all three places: your app's start command, the nginx upstream, and the container mapping if you have one. Three numbers on a sticky note catch more 502s than blindly raising a timeout ever will."

The port table: which number each platform actually expects

Tutorials will tell you Beanstalk uses 8080. Other tutorials will tell you it uses 5000. Both skip the only thing that decides it: the platform. Read your environment's platform branch, then make the process listen where that platform's nginx forwards:

PlatformWhere nginx forwards by defaultHow the mismatch usually appearsFirst fix
Node.js (current Linux platforms)5000, unless PORT overrides itExpress listens on 3000 or 8080Read process.env.PORT
Python (WSGI)8000 is the WSGI server's common defaultGunicorn or Flask binds another portAlign the Procfile and PORT
Java SE / Spring Boot5000Spring Boot stays on its 8080 defaultserver.port=${PORT:5000}
Docker (single container)Whatever the container exposes and the platform mapsEXPOSE, the process bind and the proxy disagreeMake the listening port and the mapping agree
TomcatContainer-managed; not the Java SE JAR rulesPort assumptions copied from a Spring Boot JAR deployRead the Tomcat platform configuration

8080 is a perfectly good port when the proxy or the container is configured to expect it. It is not a Beanstalk requirement, and on the Java SE and Node.js platforms it is the wrong answer by default.

Jake: "So the number I need is written somewhere, not something I choose."

Ethan: "It is chosen by the platform you picked when the environment was created. You get to agree with it, or override it everywhere at once. Disagreeing with it in one place is this error."

Node.js: honor PORT, bind to every interface

An Express or Fastify server runs beautifully on localhost:3000 and 502s on Beanstalk because nginx is knocking on 5000. The usual cause is a hard-coded development port in server.js or in the start script. Make production read the platform's property:

const express = require("express");
const app = express();
const port = Number(process.env.PORT || 5000);

app.get("/health", (_req, res) => res.status(200).send("ok"));
app.get("/", (_req, res) => res.send("Booking page ready"));
app.listen(port, "0.0.0.0", () => {
  console.log(`Listening on ${port}`);
});

Binding to 0.0.0.0 accepts connections on the instance's interfaces, not only loopback. The process has to keep running; a start command that exits leaves nginx with no one to talk to. And package.json needs a real start script, node server.js, not the dev script you use at home with hot reload and a different port. If web.stdout.log says "Listening on 3000" and nginx points at 5000, that is the whole bug. Keep the port in one place, so two hardcoded values can never disagree.

Python: Gunicorn, the Procfile and 8000

The Python platform is the one where 5000 is wrong. Its WSGI server defaults to 8000, and a Flask or Django app that works under the development server can fail behind nginx either because Gunicorn starts on another port or because it cannot import your application at all:

# Procfile at the source-bundle root (Django)
web: gunicorn --bind :8000 --workers 3 --threads 2 project.wsgi:application

# Flask, where application.py defines an object named application
web: gunicorn --bind :8000 application:application

# prove the import works before you blame the port
python -c "import project.wsgi; print(project.wsgi.application)"

If you choose a different port in the Procfile, set the PORT property to match. A Procfile on the Python platform overrides several of the WSGI settings in the console, so read the command it actually runs rather than the setting you thought controlled it. And tell the two failures apart: an import error shows up immediately in Gunicorn's output and leaves nothing listening; a port mismatch leaves a healthy process listening in the wrong place.

Spring Boot: the 8080 that follows you to production

This is the classic. The JAR starts, Tomcat inside it announces port 8080, nginx on the Java SE platform forwards to 5000, and every customer gets a 502 while the application sits there, healthy and unreachable. Tell Spring Boot to use the platform's port, with 5000 as the fallback:

# application.properties
server.port=${PORT:5000}

# or application.yml
server:
  port: ${PORT:5000}

# Procfile beside the packaged JAR
web: java -jar application.jar

Java SE will run a single JAR on its own, but a Procfile makes the launch explicit and is required for multi-JAR layouts or custom JVM options. You can also pass --server.port=5000 on the command line; pick one authoritative place and stop. An environment property saying 5000 while an application property forces 8080 is a confusion waiting for the next deployment. If the Java process dies instead of listening on the wrong port, read the stack trace: missing database credentials, a profile that does not exist, heap settings, an incompatible runtime. Nginx cannot serve a backend that never finished starting.

Jake: "Our booking API is Spring Boot. Ethan, is this us?"

Ethan: "Your log says 'Tomcat started on port 8080'. Nginx is configured for 127.0.0.1:5000. Both of them are working perfectly and they have never met. Yes, this is us."

Docker: three layers that have to agree

A container build can succeed while the app inside never becomes reachable, because there are three ports in play: what the process binds inside the container, what the container EXPOSEs, and what the host proxy or Compose routing sends traffic to. Changing EXPOSE alone changes nothing about what the process listens on:

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV PORT=5000
EXPOSE 5000
CMD ["npm", "start"]

# test the mapping locally before blaming AWS
docker build -t eb-diagnostic .
docker run --rm -p 5000:5000 eb-diagnostic
curl -i http://127.0.0.1:5000/health

That Dockerfile is coherent only if the Node app reads PORT and listens on 5000. With Compose, the proxy upstream names a service and an internal port, web:5000, and the service had better listen there. If the local test fails, fix the image. If it passes and the environment still 502s, the suspects are the deployed image, the proxy settings, instance logs, container health and environment variables. And Docker platforms differ: a single-container environment, a Compose environment and a cluster-style platform route and log differently, so match the recipe to the platform you actually have, not to an old Dockerrun.aws.json tutorial.

The Procfile: start a process and keep it alive

A deployment can "succeed" with no usable web process when the Procfile is missing, misnamed, in the wrong place, has a typo, or starts something that exits immediately. The file is plain text, named exactly Procfile with a capital P, at the root of the zip:

# one of these, not all three
web: node server.js
web: java -jar application.jar
web: gunicorn --bind :8000 application:application

web is the main process name. Beanstalk expects it to run forever and restarts it when it dies. The commonest mistake is zipping the project folder instead of its contents, so the archive contains my-app/Procfile instead of Procfile, and the platform never sees it. Setup steps that are supposed to finish, like a database migration, are not web servers and do not belong in the web line; a script that migrates and exits is a process that "crashed" as far as the supervisor can tell.

The three logs, in order

Three questions, three files. Did the app start? Why could nginx not connect? What happened during the deployment? Read them in that order, because the answer to the first often makes the other two unnecessary:

FileQuestion it answersTypical clue
/var/log/web.stdout.log (where present)What did the web process print?"Listening on 8080", a framework exception, a crash
/var/log/nginx/error.logWhy did the proxy fail?Connection refused, upstream timeout, early close
/var/log/eb-engine.logDid deployment and process setup succeed?A failed command, a Procfile problem, a hook error
/var/log/web-1.log, web-1.error.log (some layouts)Where did a Procfile process write?Stack trace, stderr
/var/log/nginx/access.logWhich paths and statuses did nginx serve?A 502 spike, a failing health path

Filenames vary by platform branch; web.stdout.log does not exist everywhere. Use the bundle to discover what is there rather than concluding that a missing file means no errors.

# EB CLI, from an initialized project
eb logs --all

# AWS CLI: request the bundle, then fetch its temporary link
aws elasticbeanstalk request-environment-info --environment-name my-environment --info-type bundle
aws elasticbeanstalk retrieve-environment-info --environment-name my-environment --info-type bundle

Request and retrieve are two steps; the second returns a temporary link once the bundle is ready. An empty or unauthorized response usually means the wrong Region or missing permissions, not a missing environment.

Jake: "There's a deployment error from yesterday in the engine log. That must be it."

Ethan: "Find one customer request, say the one at 11:03, and match it with the nginx access and error lines at that time. Read the last successful app line before the 502. Yesterday's deployment error is less useful than the upstream error from this exact request."

What each nginx message is trying to tell you

The nginx error line, read exactly, cuts the search to one direction. These are not all "port problems":

Nginx saysIt meansLook at
connect() failed (111: Connection refused)Nothing accepted the connection on the upstream portIs the app listening, and on which port
upstream prematurely closed connectionThe app hung up while nginx was waitingCrash trace, process restarts
upstream timed outThe app did not answer in timeHow long the endpoint takes; proxy timeouts
no live upstreamsThe configured upstream set has nothing availableProxy configuration and the target process
Permission denied connecting to upstreamA local socket or OS access problemSocket and security configuration

Nginx refusing to reach 127.0.0.1:5000 while ss shows your process on 8080 is a port mismatch, concrete and done. Nginx connecting and getting an early close is a crash during the request, and the next clue is in the application log. And if the failing request never appears in nginx's access log at all, the 502 came from the load balancer, one hop earlier: look at the target group's health, not at nginx.

Health checks that turn the environment red

The site can be working for most visitors while the environment shows Severe. Enhanced health uses instance and request data, including status codes and response time, and a misconfigured load-balancer health check can mark targets unhealthy on its own, shrinking the capacity that gets traffic. The fix is a health endpoint that is boring on purpose:

  1. Add GET /health that returns 200 immediately when the process can serve traffic. No login, no session cookie, no report query.
  2. Find the environment's health-check path and the target group's matcher and timeouts.
  3. Make sure that path is reachable through the intended listener without redirects.
  4. In EC2 → Load Balancing → Target Groups, read each registered target's health reason.
  5. curl -i the normal site path and the health path and compare with the nginx access log.
  6. Give new instances time to start before calling slow startup a permanent failure; fix the slow startup separately.
curl -i https://my-environment.example.com/health
aws elasticbeanstalk describe-environment-health --environment-name my-environment --attribute-names All
aws elbv2 describe-target-health --target-group-arn TARGET_GROUP_ARN

Two traps. A health path that redirects to a sign-in page returns 302, not 200, and fails forever. A health check that queries every business table fails during a moment of database slowness even though the app could serve everything else. And when a target is unhealthy, read its reason; do not widen the matcher until a 404 counts as healthy, because that hides a broken deployment rather than fixing it.

The load balancer idle timeout: last, not first

Some 502s happen only on slow report downloads or long requests. Before you raise a timeout, identify which hop returned the status and what the backend was doing; an nginx upstream timeout and a load-balancer gateway error are different events. For an Application Load Balancer the attribute is idle_timeout.timeout_seconds, default 60 seconds, configurable from 1 to 4,000, and separate from the client keep-alive duration:

aws elbv2 describe-load-balancer-attributes --load-balancer-arn LOAD_BALANCER_ARN
aws elbv2 modify-load-balancer-attributes --load-balancer-arn LOAD_BALANCER_ARN \
  --attributes Key=idle_timeout.timeout_seconds,Value=120

A longer timeout is right for a response that genuinely takes that long. It is not a substitute for matching a port, fixing a crash or keeping health checks fast. If a request takes minutes because it builds a report, move the work to a background queue and return a status ID at once; Jake's booking form should never hold a customer's browser open while something else generates a spreadsheet.

Environment properties without a second port conflict

Two declarations of the same setting, a local .env and a Beanstalk environment property, is how ports drift. Production only sees what is in the instance's environment. Set properties in Configuration, in the software section for your platform, or from the CLI:

aws elasticbeanstalk update-environment --environment-name my-environment \
  --option-settings Namespace=aws:elasticbeanstalk:application:environment,OptionName=PORT,Value=5000

Set PORT only when it is the right way to align the proxy and the app, and change it knowing the environment will update and restart. Credentials go in a secrets integration, not in properties pasted from an article. And an app that crashes on startup because it cannot reach its database will not be fixed by any port.

Deployed fine, process failed

Events can say the version deployed while the process crashes two seconds later. Deployment, process supervision and health are three separate observations. Compare the Events timestamps with eb-engine.log: a Procfile pointing at a JAR that does not exist, a missing module or a hook failure shows up in the engine trace; an app that starts and then throws on its first request shows up in the application output.

eb events --follow
eb logs --all

# on the instance
sudo tail -n 150 /var/log/eb-engine.log
sudo tail -n 150 /var/log/nginx/error.log
sudo ls -lah /var/log/*web*

A platform hook that exists in your repository has not necessarily run: its folder, its permissions, its line endings and its platform compatibility all decide that. Keep the last known-good bundle so you can diff it against the broken one.

Custom nginx configuration

If someone added a custom nginx config just before the 502 began, that is the suspect. On AL2 and AL2023 platforms, proxy extensions belong under .platform/nginx/; the legacy AL1 paths from old tutorials are ignored or break things. A custom file can hard-code an upstream port, override a server block or drop includes the platform needs. Inspect the effective config on an instance:

sudo nginx -t
sudo nginx -T | grep -n -A6 -B6 proxy_pass
sudo ss -ltnp

A literal proxy_pass http://127.0.0.1:8080 while the Java app listens on 5000 is the bug; nginx -t catches a syntax error from a bad include before you restart anything. Prefer small additive snippets to a full proxy replacement.

Worked example: the booking API moves from 8080 to 5000

Jake's Spring Boot API ran on 8080 on Ethan's laptop. Packaged as a JAR and deployed to the Java SE platform, it logged "Tomcat started on port 8080" while nginx forwarded to 127.0.0.1:5000. Both running, never connected. The fix was server.port=${PORT:5000} in the application properties and an explicit web: java -jar application.jar Procfile. The new startup log said 5000; curl -i http://127.0.0.1:5000/health answered 200 in about twenty milliseconds, while a real booking request took around two hundred, which is the point of a separate health path.

If a 502 had survived the port change, the next read would have been the failing request in error.log and a check that the process was still alive. And if the site had started returning 500 instead of 502, that would have been progress: nginx reached the app, and the app itself failed the request. A changed status code is the next clue, not a setback.

Jake: "It's a 500 now. Worse?"

Ethan: "Better. 502 meant nobody answered the door. 500 means your app answered and said something went wrong inside. We are through the door. Now it is your code's turn to explain itself, in its own log."

Application vs environment, Beanstalk vs EC2, Beanstalk vs ECS

Three distinctions that come up while fixing this. The application (say, phone-shop) is a logical folder of versions and environments; the environment (phone-shop-prod) is the running deployment with its platform, properties, load balancer and health. When production 502s, inspect the production environment: the same JAR can be fine in staging and broken in prod because the two environments have different PORT values, platform branches, security groups or health paths, and the JAR may need no change at all.

Beanstalk vs plain EC2: Beanstalk runs on EC2 underneath and automates deployment, platform setup, health and resources; on plain EC2 you own the proxy, the process manager, the release workflow and the scaling. A 502 is not evidence either way: an app listening on the wrong port fails behind any reverse proxy on any stack.

Beanstalk vs ECS: ECS is container orchestration with task definitions and explicit service control; Beanstalk is an application-environment workflow that can also run Docker. Many independent services with detailed scheduling lean ECS; one web app that needs a straightforward deployment fits Beanstalk. Neither forgives a wrong port: an ECS target group still has to reach the container's listening port, exactly as nginx has to reach yours.

What two hours of 502 costs a shop

A gateway error does not pause the bill. Instances and load balancers keep charging while unhealthy. The bigger number is the shop's: if Jake normally takes 8 bookings an hour and the outage lasts two hours, up to 16 attempts hit the error page; at an average booking of $35, that is $560 of exposed revenue, hypothetical but the right order of magnitude. Infrastructure prices vary by instance type, Region and traffic, so use your own bill for the cloud side. The technical priority is to restore responses without a risky change that prolongs the incident; our idle load balancers page covers the standing waste that an unhealthy environment keeps paying for.

When the simple fixes do not hold

Port matched, Procfile fixed, health path fast, and the site still 502s now and then. Look at which instance served the failing request. One unhealthy instance in an environment of three has a bad deployment, a different initialization or exhausted resources, and the aggregate health hides it. Compare listening ports and deployed versions per instance. Check for memory exhaustion or restarts under load: nginx reports a premature close when the app exits mid-response, and a JVM out-of-memory, a crashed Python worker or a Node fatal exception needs fixing before any proxy tuning. And check HTTPS termination and forwarded headers; an app that redirects endlessly or rejects proxied requests produces symptoms that look like, but are not, a connection-refused 502.

For the intermittent case, go to the target group and read each target: ID, zone, health reason, and whether it turns unhealthy right after a deployment. In the Beanstalk Health view, find the one instance with the higher 5xx rate. A rolling deployment also means old and new versions coexist for a while, so note which version was expected at the moment of each failed request.

aws elbv2 describe-target-health --target-group-arn TARGET_GROUP_ARN

# only on an instance you are authorized to inspect
sudo ss -ltnp
curl -si http://127.0.0.1:5000/health
sudo tail -n 100 /var/log/nginx/error.log

Do not terminate instances or rebuild the environment as a first move; that destroys the logs that explain the difference. Bundle the logs first, then replace the odd instance through the platform's normal workflow.

After a platform upgrade

If the 502 started the moment you updated the platform branch, a runtime or proxy change exposed an assumption. AL2 and AL2023 use proxy-extension paths that differ from the old Amazon Linux AMI layout, and 2026 platform releases have included nginx updates. That does not move your application's port; it means the old and new effective nginx config should be compared rather than assumed identical. Record the previous working platform version and the failing one, compare Procfile, port properties, startup messages and custom overrides, and read the release notes for your exact branch, not for a different language's platform. Some failures start as a startup exception after the runtime changed, after which nginx reports connection refused; the 502 is the last link in that chain.

A rollback that restores more than the homepage

Deploying the last known-good version is a reasonable recovery once the logs are saved and the problem is not an infrastructure setting. A version rollback does not undo environment properties, load balancer attributes, database state or a platform upgrade, so after it, open the real booking route, the health path and a slow or authenticated route, and watch instance health and 5xx rates through the next deployment. If the rollback restores the port and the new artifact does not, diff their startup commands. If both versions fail after a property change, the fault is in the environment, and rebuilding identical artifacts will never find it.

Jake: "I would rather roll back than keep experimenting on the page customers are using."

Ethan: "Restore what you know worked, save the logs, and make the next change somewhere customers are not depending on. That is not giving up. That is the order of operations."

Five moves that make a 502 last all afternoon

Every one of these feels like progress at the time. Each one either hides the evidence or adds a second problem to the first:

  • Raising the load balancer timeout first. A process that is not listening will not start listening because you waited 120 seconds instead of 60. Find the hop, then decide whether a timeout is even involved.
  • Opening the security group wide. The request already reached nginx; the failure is behind nginx, on localhost. No inbound rule touches that hop, and now you have a security problem to undo later.
  • Copying a port number from a tutorial for a different platform. 8080 from a Spring Boot guide, 3000 from an Express one, 5000 from a Node-on-Beanstalk one: each is right somewhere and wrong here unless it matches your platform's nginx.
  • Zipping the project folder instead of its contents. The Procfile ends up one level deep, the platform never sees it, and the "fix" you deployed never ran.
  • Terminating the instance or rebuilding the environment as step one. The nginx error log and the engine log were on that instance. Download the bundle first; rebuild last.

Jake: "I did two of those before I called you."

Ethan: "Everyone does. The timeout and the security group are the two buttons that are closest to hand in the console. Neither of them is anywhere near the problem."

What to collect if it stays broken

One timestamped failing request and everything needed to locate it: platform branch, Region, environment name, application version, current health, the nginx error lines, the application output and the engine-log excerpt. Add whether the same version runs elsewhere, the expected and actual ports, the exact health-check path, and whether anyone changed the ALB timeout or the nginx config. Strip credentials and customer details. Support can speak to platform behavior and managed infrastructure; your application's exceptions and dependencies remain yours, and one failing request with its upstream target makes that conversation short.

The exact Elastic Beanstalk 502 questions people search for

How do I fix elastic beanstalk 502 bad gateway?

Read /var/log/nginx/error.log and the app's stdout log, compare the port nginx forwards to with the port your process actually listens on, then fix the start command, the Procfile or the PORT property so they agree. Confirm the health-check path returns 200.

Why does elastic beanstalk nginx 502 happen after deployment?

The new version did not start, bound a different port than nginx expects, or crashed after launch. Compare eb-engine.log, the web process output and the nginx upstream errors for that deployment's time window.

How do I fix elastic beanstalk 502 bad gateway spring boot?

On the Java SE platform nginx forwards to 5000 and Spring Boot defaults to 8080. Set server.port=${PORT:5000} in application.properties, add a Procfile with web: java -jar application.jar, and redeploy.

Should my Elastic Beanstalk app listen on 8080 or 5000?

It depends on the platform. Node.js and Java SE platforms proxy to 5000; the Python WSGI server defaults to 8000; Docker depends on your mapping. Read your environment's PORT property and nginx upstream rather than a tutorial.

Does Elastic Beanstalk use port 5000 by default?

Several current Linux platforms, including Node.js and Java SE, forward to 5000 by default. It is not universal: Python WSGI commonly uses 8000, and Docker and Tomcat platforms follow their own configuration.

How do I write an Elastic Beanstalk Procfile?

Create a plain-text file named Procfile, capital P, at the root of the source bundle, containing one long-running process line such as web: node server.js or web: java -jar application.jar. Zip the folder's contents, not the folder.

Where is web.stdout.log in Elastic Beanstalk?

In the full log bundle at /var/log/web.stdout.log on platforms that use that name. Other platforms write web-1.log and web-1.error.log or other process-specific files, so check the bundle's actual file list.

Where is the nginx error.log on Elastic Beanstalk?

At /var/log/nginx/error.log on the Linux platforms that use nginx. Read it through the downloaded log bundle, a CloudWatch log stream if streaming is configured, or on the instance.

What does eb-engine.log show?

/var/log/eb-engine.log records the platform's deployment and configuration work: the commands it ran, hook results and failures. It explains why an app did not start, not what the app did afterwards.

Why is my Elastic Beanstalk environment red but the homepage loads?

Some instances or requests are unhealthy, the health check uses a different path than your visitors, or response times and error rates are poor. Open the enhanced health details and the load balancer's target health.

How do I set elastic beanstalk environment variables?

In the console under Configuration, in the software section for your platform, or with aws elasticbeanstalk update-environment using the aws:elasticbeanstalk:application:environment namespace. Changing one restarts the environment.

Does increasing the load balancer idle timeout fix a 502?

Only when the 502 is a genuine timeout on a slow request. The ALB default is 60 seconds, range 1 to 4,000. It cannot fix a process that is not listening or has crashed.

What is the difference between Elastic Beanstalk application vs environment?

The application is a logical container that groups versions and environments. An environment is one running deployment with its own platform, configuration, resources and health. You fix environments.

What is the difference between elastic beanstalk vs ec2?

Elastic Beanstalk automates deployment, platform setup, health reporting and resource management on top of EC2 instances. Plain EC2 leaves the proxy, process manager, releases and scaling to you.

When should I choose elastic beanstalk vs ecs?

Beanstalk suits an application-centric deployment of one or a few web apps. ECS suits many containerized services with explicit scheduling and task-level control. Both still need the right port and a fast health check.

How do I tell an nginx 502 from an ALB 502?

Check whether the failing request appears in nginx's access log. If it does, nginx produced the 502 and the app is the suspect. If it does not, the load balancer produced it; look at target health and listeners.

Jake's 502 was a Spring Boot JAR answering on 8080 to an nginx that asked on 5000, which is to say it was the most common Beanstalk 502 there is. One property, one Procfile, and the booking page came back before lunch. The sticky note by the till now reads "5000, 5000, 5000" with the three places underlined, and the health path returns 200 in twenty milliseconds without asking the database how it feels.

📌 If you keep one line from this page

Fix the port nginx actually calls, not the port a tutorial happens to mention.

Three numbers must agree: the app's bind, nginx's upstream, the PORT property. Then a health path that answers in milliseconds.

Revision note. Written October 9, 2026, with the nginx proxy defaults for Node, Java and Python checked against the platform docs. There is many a slip between the cup and the lip, and this one happens between nginx and your process.

Related