osteel's blog Web development resources

Docker for local web development, part 6: expose a local container to the Internet

Been here before?

You can also subscribe to the RSS or Atom feed, or follow me on Twitter.

Local container exposed to the Internet

In this series

Subscribe to email alerts at the end of this article or follow me on Twitter to be informed of new publications.

In this post


While using Docker for local development allows us to replicate a production environment as closely as possible in a self-contained way, in some instances exposure to the outside world is unavoidable.

Typical use cases include testing a third-party service's webhook (like a transaction confirmation from a payment gateway), or showing a project's advancement to a client. Sure, you could use a staging environment for that, but you might not be in a position to offer one, and in some cases it would feel akin to squashing a fly with a sledgehammer.

Wouldn't it be more practical if you could make your local environment public instead?

Thankfully, this isn't a new issue and several services offer to address it. This article focuses on one that's arguably become a reference over time: Ngrok.

The assumed starting point of this tutorial is where we left things at the end of the previous part, corresponding to the repository's part-5 branch.

If you prefer, you can also directly checkout the part-6 branch, which is the final result of today's article.


Ngrok is an online service that essentially allows developers to create secure tunnels to channel traffic from a public URL to a local address. It offers paid plans but also comes with a generous free version that doesn't even require an account for HTTP tunnels. Since our setup runs on HTTPS, however, we'll need to sign up in order to obtain an authentication token. Don't worry though, it only takes a few seconds and doesn't require credit card details.

If you really don't want to open an account, there will be an information box towards the end of this tutorial explaining how to use the subscription-free plan instead.

Installing and configuring

There is no official Docker image for Ngrok, but the community stepped up and made a few of them available.

We'll use Werner Beroux's today, for which we need to create a service in docker-compose.yml:

# Ngrok Service
  image: wernight/ngrok:latest
    - 4040:4040
    NGROK_PORT: nginx:443
    - nginx

Ngrok comes with an interface served on the container's port 4040 which we mapped to localhost's, and since Nginx will once again be our entry point for the traffic, we made sure its container is started before Ngrok's using depends_on.

That leaves us with a bunch of environment variables:

  • NGROK_PROTOCOL can either be http or tcp – in our case, we need the former;
  • NGROK_PORT is a bit misleading, as it's actually not only the port but also the host, which is nginx:443 in our case (if no host is specified, localhost is implied);
  • NGROK_AUTH is the authentication token I mentioned earlier, whose value is another environment variable that we'll set in a minute.

That's all we need for today, but you might want to have a look at the other environment variables listed in the image's documentation.

We now need to set the authentication token, which you will find in your dashboard. If you remember, in the first part of this series we created a .env file at the root of the project, alongside docker-compose.yml. So far we've only used it to specify a project name (to avoid container name collisions with other projects), but that's also where we'll set the token.

Open the file and change its content to this one, replacing the <YOUR TOKEN> placeholder with... your token:


For good measure, we should also update .env.example, to make it clear for future users that a value is expected:


This gives you the flexibility to either set a token in .env.example for everyone to use, or to ask each developer to create their own Ngrok account and complete their local .env file accordingly.

Of course, you could set the token in docker-compose.yml directly, but I wanted to show you another way to use the project's .env file.

The last thing we need to do is to update the Nginx server configuration in .docker/nginx/conf.d/backend.conf:

server {
    listen      443 ssl http2;
    listen      [::]:443 ssl http2;
    server_name backend.demo.test *.ngrok.io;
    root        /var/www/backend/public;

Only the bit in bold needs to be added, the rest of the file remains as it is. Save it and restart the project in order to create the Ngrok container and to reload the Nginx configuration:

$ demo restart

Open localhost:4040 in your browser:

Ngrok interface

Two URLs are available – spot the HTTPS one and open it in a new tab, appending /api/hello-there to the end:

Ngrok Hello There

Our API is now available publicly! Now go back to the other tab – you should see something like this:

Ngrok inspection

You can now use that URL with third-party services or distribute it as much as you want, as long as the Ngrok container is running. The corresponding traffic will appear in the interface, where you can inspect it and replay requests at will. Handy!

There's only one little downside: the URL will expire after 8 hours, and it will change every time the Ngrok container is restarted. That's always been fine by me as I usually need tunnels for quick tests only, but you can always look into the paid plans if that's an issue for you – they come with custom and permanent subdomains, among other things.

Don't want to open an account?If you really don't want to subscribe, you can use the account-free version instead, but you will need to make the backend available on port 80. To do so, change port 443 to port 80 in the Ngrok service in docker-compose.yml and remove the NGROK_AUTH environment variable. Create a server block dedicated to *.nginx.io in the backend.conf Nginx configuration, listening on port 80 instead of port 443. Restart the project, and you should be able to use the HTTP URL provided by Ngrok.


That's it! That's all it takes to make a local container available to the Internet.

This article once again demonstrates how easy it can be to leverage a piece of technology in minutes with Docker, with no prior knowledge. There's no need to worry about messing up the installation or cluttering your local setup – as long as there's a Docker image for it (and most popular technologies have one), it doesn't take much to give it a go, quickly and safely.

This is also a good example of when it is OK to rely on the community, whenever the software issuer or core team hasn't provided an image yet (that is not to say that it is usually a bad idea to trust the community, simply that if there is an official image available, you're probably better off using it instead).

In the next article, we'll talk about another key Docker concept: multi-stage builds. We'll use it to create a worker for our API, to listen to and consume messages from a queue.

You can subscribe to email alerts below to make sure you don't miss it, or you can also follow me on Twitter where I will share my posts as soon as they are published.

Enjoying the content?

You can also subscribe to the RSS or Atom feed, or follow me on Twitter.

Last updated by osteel on :: [ tutorial docker ngrok ]