DEV Community

AttractivePenguin
AttractivePenguin

Posted on

Docker Container Exits Immediately? Here's the Fix

Docker Container Exits Immediately? Here's the Fix

You've just run docker run and—nothing. The container starts, runs for a split second, and exits with code 0. What happened?

This is one of the most common Docker frustrations, especially for developers new to containerization. The good news? It's usually easy to fix once you understand what's happening.

Why Your Container Keeps Stopping

Here's the fundamental rule: A Docker container lives only as long as its primary process (PID 1).

When that process completes or crashes, the container stops. This is by design—containers aren't virtual machines. They're isolated processes with their own filesystem.

Common causes:

  1. Short-lived commands — Running a script that finishes immediately
  2. Background processes — Starting a daemon that daemonizes itself (like nginx or redis-server)
  3. Crashes — The application encounters an error and exits
  4. Missing arguments — The container expects runtime arguments you didn't provide

Let's walk through diagnosing and fixing each scenario.

Step 1: Diagnose with Docker Logs

Before fixing anything, find out why the container stopped:

# Get the container ID (even stopped ones)
docker ps -a

# View the logs
docker logs <container_id>
Enter fullscreen mode Exit fullscreen mode

The output usually tells the story:

Hello from Node.js!
Server started on port 3000
Connection established
Process completed
Enter fullscreen mode Exit fullscreen mode

If you see "Process completed" or similar, your app ran and finished normally. If you see error messages, it crashed. If you see nothing, the process might have daemonized itself.

Pro tip: Use docker logs -f <container_id> to follow logs in real-time while the container is running.

Step 2: Fix Based on Your Scenario

Scenario A: Your Script Finishes Too Quickly

You're running a Node.js script, Python file, or shell script that does its job and exits.

Wrong approach:

docker run my-image node script.js
# Container exits immediately
Enter fullscreen mode Exit fullscreen mode

Solution 1: Keep it running with an interactive shell

docker run -it my-image sh
# Now you're inside, can run commands manually
Enter fullscreen mode Exit fullscreen mode

Solution 2: Add a long-running process

FROM node:20-alpine
COPY script.js .
CMD ["sh", "-c", "node script.js && tail -f /dev/null"]
Enter fullscreen mode Exit fullscreen mode

The tail -f /dev/null trick keeps the container alive after your script finishes—useful for debugging or when you need the container to persist.

Solution 3: Run as a service

FROM node:20-alpine
COPY server.js .
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

Where server.js is a long-running server (Express, Fastify, etc.) that doesn't exit on its own.

Scenario B: Background Process Daemonizes

Some applications start as daemons by default—they fork into the background and the main process exits.

Wrong approach:

# nginx daemonizes by default
CMD ["nginx"]
Enter fullscreen mode Exit fullscreen mode

Solution: Run in foreground mode

Most daemons have a flag to stay in the foreground:

# Nginx
CMD ["nginx", "-g", "daemon off;"]

# Redis
CMD ["redis-server", "--daemonize", "no"]

# Apache
CMD ["apache2ctl", "-D", "FOREGROUND"]
Enter fullscreen mode Exit fullscreen mode

This is the recommended pattern—let Docker manage the process lifecycle, not the application.

Scenario C: Application Crashes

If your logs show errors, fix the underlying issue:

# Check logs for stack traces
docker logs <container_id>

# Run interactively to debug
docker run -it --rm my-image sh
# Now you can run commands, check files, etc.
Enter fullscreen mode Exit fullscreen mode

Common crash causes:

  • Missing environment variables
  • Database connection failures
  • Permission issues (try not running as root)
  • Missing dependencies (did you install them in the Dockerfile?)

Step 3: Use Restart Policies

Even with correct configuration, things can go wrong. Docker restart policies automatically restart containers when they stop.

# Always restart
docker run --restart always my-image

# Restart unless manually stopped
docker run --restart unless-stopped my-image

# Restart only on failure (max 5 retries, delay 10s)
docker run --restart on-failure:5 my-image
Enter fullscreen mode Exit fullscreen mode

Restart policy comparison:

Policy Starts on daemon start? Restarts on failure? Use case
no No No Development, testing
on-failure No Yes (with limit) Services that might crash
always Yes Yes Production services
unless-stopped Yes (if running before) Yes Production services (preferred)

For most production workloads, unless-stopped is the best choice—it restarts on failure and on Docker daemon restart, but respects manual stops.

Real-World Scenarios

Development: Keep a Database Running

# docker-compose.yml
version: '3.8'
services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: development
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

The restart: unless-stopped ensures your database comes back up after crashes or system reboots.

CI/CD: Run Tests and Exit

For CI pipelines, you want the container to exit:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "test"]
Enter fullscreen mode Exit fullscreen mode

The test runs, results are collected, and the container exits—exactly what CI needs.

Production: Graceful Shutdown

For long-running services, handle shutdown signals properly:

// Node.js example
const server = app.listen(3000);

process.on('SIGTERM', () => {
  console.log('Shutting down gracefully...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});
Enter fullscreen mode Exit fullscreen mode

Docker sends SIGTERM on stop, waits (default 10s), then sends SIGKILL. Handling SIGTERM lets your app finish in-flight requests.

FAQ

Why does my container exit with code 0?

Code 0 means "success"—your process completed normally. This usually means you're running a one-off command instead of a long-running service. Either change your entrypoint to a server/daemon, or add && tail -f /dev/null to keep it alive.

Why does my container exit with code 1?

Code 1 indicates an error. Run docker logs <container_id> to see what went wrong. Common causes: missing dependencies, invalid configuration, database connection failures.

How do I keep a container running for debugging?

Use an interactive shell:

docker run -it --rm my-image sh
# or
docker run -it --rm --entrypoint sh my-image
Enter fullscreen mode Exit fullscreen mode

The -t flag allocates a pseudo-TTY (required for shell interaction), and -i keeps STDIN open.

What's the difference between CMD and ENTRYPOINT?

  • CMD: Default arguments, easily overridden with docker run my-image arg1 arg2
  • ENTRYPOINT: The executable, harder to override (needs --entrypoint flag)

Best practice: Use ENTRYPOINT for the executable and CMD for default arguments:

ENTRYPOINT ["node"]
CMD ["server.js"]
Enter fullscreen mode Exit fullscreen mode

Now docker run my-image runs node server.js, but docker run my-image other.js runs node other.js.

My container works locally but exits in production. Why?

Common differences:

  • Environment variables: Are all required env vars set in production?
  • Networking: Can the container reach required services?
  • Volumes: Are volume paths correct and accessible?
  • Permissions: Is the container running as a user that can access required files?

Always test with production-like configuration locally:

docker run --env-file .env.production -v ./data:/app/data my-image
Enter fullscreen mode Exit fullscreen mode

Conclusion

When your Docker container exits immediately, remember the golden rule: PID 1 determines container lifetime.

Your fix depends on the scenario:

  • Script finishes too fast? Add a server or use tail -f /dev/null
  • Daemon daemonizes? Use foreground mode flags
  • App crashes? Check logs and fix the root cause
  • Want resilience? Add restart policies

The docker logs command is your best friend—it almost always reveals what went wrong. Start there, apply the appropriate fix, and you'll have containers that stay running when they should (and exit cleanly when they shouldn't).

Next time your container disappears, don't panic. Check the logs, fix PID 1, and get back to shipping.

Top comments (0)