Run Stripe CLI in Docker Compose.

Published on February 18, 2026
Written by Victor Cobos

Intro

We’re currently improving the payment flow in one of our projects, where Stripe handles all transactions. Whenever you integrate Stripe, webhooks quickly become essential. Events like a successful payment, a failed charge, or a completed checkout session need to be reflected inside your own application to keep everything in sync.

The challenge? When developing locally, Stripe has no direct way to reach your machine. Your app isn’t publicly accessible, so webhook events can’t be delivered without help.

That’s where the Stripe CLI comes in. It provides a listen command that connects to Stripe and forwards webhook events directly to your local application. A typical command looks something like this:

stripe listen \
    --forward-to localhost/stripe/webhooks

We’re not big fans of installing project-specific tools globally on our machines. It makes it harder to keep tooling consistent across the team, and onboarding new developers becomes more complicated than it needs to be. Since Stripe provides an official Docker image for their CLI, we decided to run it through Docker instead.

šŸ› ļø Setup

Since our entire application runs inside Docker, it made sense to treat Stripe CLI like any other infrastructure component. Instead of running it manually, we defined it as a dedicated service in our compose.yml.

That means when we start our environment, Stripe webhook forwarding starts as well.

Here’s what that configuration looks like:

stripe-cli:
  image: stripe/stripe-cli:latest
  restart: unless-stopped
  env_file:
    - .env
  command: listen --api-key ${STRIPE_API_KEY} --forward-to http://web:3000/stripe/webhooks --skip-update --skip-verify
  depends_on:
    - web

The key detail in this configuration is the --forward-to option, especially the URL http://web:3000/stripe/webhooks. Our application runs inside Docker, and its service name in compose.yml is web. Docker Compose creates an internal network where services can reach each other using their service names as hostnames. That’s why we use web instead of localhost.

ā—Note: If the app runs outside Docker, this value must be changed to something like http://localhost:3000/stripe/webhooks or http://host.docker.internal:3000/stripe/webhooks

The listen command connects the CLI to Stripe using the secret key from our .env file via --api-key. The --forward-to option tells the CLI where webhook events should be delivered. We also include --skip-update to avoid update checks on startup and --skip-verify to simplify local development when using HTTP or self-signed certificates.

Why depends_on: web? This ensures the Stripe CLI container waits until the web service starts before attempting to forward events. Without this, Stripe might start before your app is ready to receive webhooks.

Final Thoughts

If your application runs in Docker, your tooling should run there too.

Keeping Stripe CLI inside Docker means we don’t rely on global installations, local versions, or manual setup steps. Every developer on the team runs the exact same version, with the same configuration, wired into the same network. Onboarding becomes faster, debugging becomes simpler, and ā€œit works on my machineā€ becomes far less likely.

It also keeps our machines clean. No extra binaries, no forgotten login sessions, no version mismatches after a system update. The Stripe CLI becomes just another part of our infrastructure — predictable, reproducible, and easy to manage.

Could we run stripe listen in a separate terminal window locally? Of course. But then we’d need to install it, keep it updated, log in, remember to start it, and make sure everyone else does the same. That’s a lot of small moving parts for something that Docker can handle automatically.

With this setup, docker compose up -d starts everything — app, database, and webhook forwarding. One command. One environment. Zero surprises.

And once you get used to that, going back to a globally installed Stripe CLI feels a bit like installing Node manually without a version manager. Sure, it works… but why would we? šŸ˜„

References

Subscribe to get future articles via the RSS feed .