Skip to main content

How to create multidomain web applications with Podman and Nginx

Learn how to use Podman and Nginx to work with applications from different domains on the same host.
Image
Red and yellow doors on a brick building

Photo by Robert Anasch on Unsplash

Managing different applications from different domains on the same host can be difficult when using different ports. When a colleague suggested I write an article about using Podman to solve that problem, possibly using a reverse proxy, I jumped at the chance.

[ Download now: Podman basics cheat sheet ]

I recently wrote an article about configuring a container to start automatically as a systemd service, which expanded on an idea from ​​my previous article about creating fast, easy, repeatable containers with Podman and shell scripts. Thanks to my colleague Jason, I'm expanding on another idea: In this article, I will explain how to use Podman and Nginx to work with applications from different domains on the same host.

Basic assumptions

Why is there a need for a reverse proxy for this proposed scenario? Because of Podman's inherent way of working. To clarify, consider the following scenario.

In a traditional environment, to run a given web application, you'd probably set aside a dedicated server configured with all the necessary web tools for that application. That application would be given a fully qualified domain name (FQDN) which would, in turn, respond to a specific DNS server and domain server. To have a second or third application answering to different DNS and domain servers, you'd probably do the same steps with servers dedicated to each application. I don't need to say how laborious, tiring, tedious, and impractical this is.

Now that you're navigating the ocean of containers, you know you can do the same thing using just a single host and multiple containers running on different high ports on that same host. This is a wonderful solution for most of your infrastructure problems. But now you have a new dilemma: Each new container you run in a different domain uses a different port on your host to communicate with the outside world, so you must manage several different ports for each application. Now you're back in that same tiresome place you started.

You know there are several solutions proposing to solve precisely this type of problem, the so-called reverse proxies. But how can you make it work with Podman and its various containers inside your host? This article will answer that question!

I'll illustrate how to use Nginx to make applications responding on different domains all use the same port on your host to communicate with the outside world. For brevity's sake, I'll omit certain basic configuration steps, but not the main ones.

[ Get hands on with Podman in this tutorial scenario. ]

Configuring and deploying your web applications with Podman

Start by pulling the needed container images for this configuration:

$ podman pull docker.io/library/httpd

$ podman pull docker.io/library/nginx

$ podman image ls
REPOSITORY                   TAG         IMAGE ID      CREATED      SIZE
..output omitted…
docker.io/library/nginx      latest      3f8a00f137a0  2 weeks ago  146 MB
docker.io/library/httpd      latest      3a4ea134cf8e  2 weeks ago  150 MB

Then create two different directories to host your web app data. In this case, I'll work with two simple HTTPD webpages:

$ mkdir syscom sysorg

$ ls -l
total 32
...output ommited...
drwxr-xr-x. 2 localuser localuser    6 fev 27 17:08 syscom
drwxr-xr-x. 2 localuser localuser    6 fev 27 17:08 sysorg

I'll create two index.html files, one for the sysadmin.com app and the other for the sysadmin.org app:

$ cat << EOF > ./syscom/index.html
<html>
  <header>
    <title>SysAdmin.com</title>
  </header>
  <body>
    <p>This is the SysAdmin website hosted on the .com domain</p>
  </body>
</html>
EOF

$ cat << EOF > ./sysorg/index.html
<html>
  <header>
    <title>SysAdmin.org</title>
  </header>
  <body>
    <p>This is the SysAdmin website hosted on the .org domain</p>
  </body>
</html>
EOF

This is all I need. Now I can run my HTTPD containers hosting these two different webpages in different domains:

$ podman run --name=syscom -p 8080:80 -v $HOME/syscom:/usr/local/apache2/htdocs:Z -d docker.io/library/httpd
4b4c00f0faa5c6cbc5a8d21d91ac5b4dbb25b1024db8477de313824bc1775db3

$ podman run --name=sysorg -p 8081:80 -v $HOME/sysorg:/usr/local/apache2/htdocs:Z -d docker.io/library/httpd
ff1d8bae380ab9b57418b09b77b875cff5d52ee58707ea1f100a6bea40156154

$ podman ps
CONTAINER ID  IMAGE                           COMMAND           CREATED         STATUS         PORTS                 NAMES
4b4c00f0faa5  docker.io/library/httpd:latest  httpd-foreground  15 seconds ago  Up 14 seconds  0.0.0.0:8080->80/tcp  syscom
ff1d8bae380a  docker.io/library/httpd:latest  httpd-foreground  9 seconds ago   Up 8 seconds   0.0.0.0:8081->80/tcp  sysorg

Keep in mind that I'm not going into the details of DNS and domain configuration externally, but for ease of demonstration, I've included these FQDNs in my /etc/hosts file specifying the same IP for both.

Note that if I try to access both through the default port 80, neither of them responds since, by default, the operating system security policies for Podman allow me to configure only high-access ports for these applications, so the sysadmin.com application responds on port 8080 while the sysadmin.org application responds on port 8081 from the same host:

$ curl http://sysadmin.com
curl: (7) Failed to connect to sysadmin.com port 80 after 1 ms: Connection refused

$ curl http://sysadmin.com:8080
<html>
  <header>
    <title>SysAdmin.com</title>
  </header>
  <body>
    <p>This is the SysAdmin website hosted on the .com domain</p>
  </body>
</html>

$ curl http://sysadmin.org
curl: (7) Failed to connect to sysadmin.org port 80 after 0 ms: Connection refused

$ curl http://sysadmin.org:8081
<html>
  <header>
    <title>SysAdmin.org</title>
  </header>
  <body>
    <p>This is the SysAdmin website hosted on the .org domain</p>
  </body>
</html>

Now the reverse proxy magic with Nginx begins!

[ Learning path: Getting started with Red Hat OpenShift Service on AWS (ROSA)

Reverse proxying your apps to the same port with Nginx

First, create a directory to contain all your configuration files for Nginx and enter it:

$ mkdir nginx

$ cd nginx/

Inside this directory, create three different files:

  • The default.conf file, which holds the default Nginx configuration
  • The syscom.conf file, which holds the configuration for the sysadmin.com application
  • The sysorg.conf file, which holds the configuration for the sysadmin.org application

For each domain, use server_name to define the domain and proxy_pass to map the container, also specifying the IP address of the host machine and the mapped port for each container. Now all of them are going to listen on the 80 port using your reverse proxy as a gateway:

$ cat << EOF > default.conf
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}
EOF

$ cat << EOF > syscom.conf
server {
  listen 80;
  server_name sysadmin.com;

  location / {
    proxy_pass http://192.168.1.30:8080;
  }
}
EOF

$ cat << EOF > sysorg.conf
server {
  listen 80;
  server_name sysadmin.org;

  location / {
    proxy_pass http://192.168.1.30:8081;
  }
}
EOF

Making this work requires taking advantage of the include /etc/nginx/conf.d/*.conf parameter of the /etc/nginx/nginx.conf file inside the Nginx container, which allows loading modular configuration files inside from the /etc/nginx/conf.d/ directory. When it runs, these files will be mounted in this directory inside the container.

Now it's time to run the Nginx container with the proper parameters. The first time you run it, however, especially running rootless containers, you may receive the following error message:

$ podman run --name=nginx -p 80:80 -v $HOME/nginx:/etc/nginx/conf.d:Z -d docker.io/library/nginx
Error: rootlessport cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), or choose a larger port number (>= 1024): listen tcp 0.0.0.0:80: bind: permission denied

This is a standard and expected security measure. To work around this and allow the Nginx container to run using the low port 80 at runtime, run:

$ sudo sysctl net.ipv4.ip_unprivileged_port_start=80
net.ipv4.ip_unprivileged_port_start = 80

Siren alert: Proceed with this setting cautiously, as it could create a vulnerability. After the container runs, you can return this setting to the default value of 1024.

Now run the container again and see what happens:

$ podman run --name=nginx -p 80:80 -v $HOME/nginx:/etc/nginx/conf.d:Z -d docker.io/library/nginx
a6575989327eb14b9d980505832e8b5600e17248667feba487c38c1792274b99

$ podman ps
CONTAINER ID  IMAGE                           COMMAND               CREATED         STATUS         PORTS                 NAMES
4b4c00f0faa5  docker.io/library/httpd:latest  httpd-foreground      30 minutes ago  Up 30 minutes  0.0.0.0:8080->80/tcp  syscom
ff1d8bae380a  docker.io/library/httpd:latest  httpd-foreground      30 minutes ago  Up 30 minutes  0.0.0.0:8081->80/tcp  sysorg
a6575989327e  docker.io/library/nginx:latest  nginx -g daemon o...  29 seconds ago  Up 28 seconds  0.0.0.0:80->80/tcp    nginx

Nice, it's running! Perform a simple test by trying to curl both of the apps' domains without specifying the higher ports and see if they both respond in port 80, like this:

$ curl http://sysadmin.com
<html>
  <header>
    <title>SysAdmin.com</title>
  </header>
  <body>
    <p>This is the SysAdmin website hosted on the .com domain</p>
  </body>
</html>

$ curl http://sysadmin.org
<html>
  <header>
    <title>SysAdmin.org</title>
  </header>
  <body>
    <p>This is the SysAdmin website hosted on the .org domain</p>
  </body>
</html>

As a final test, try to access both applications externally through a web browser and see if they respond correctly. But first, if you're running a firewall, allow access in port 80 through the HTTP service:

$ sudo firewall-cmd --add-service=http --permanent 
success

$ sudo firewall-cmd --reload
success

Here's what it looks like when I view the sysadmin.com application in a browser:

Image
Screenshot of the SysAdmin website hosted on the .com domain
(Alexon Oliveira, CC BY-SA 4.0)

And here's what the sysadmin.org application looks like:

Image
Screenshot of the SysAdmin website hosted on the .org domain
(Alexon Oliveira, CC BY-SA 4.0)

Sweet! In this final architecture, the applications are responding correctly on port 80 of the same host using Nginx as a reverse proxy for both.

Wrap up

When you need to host different applications from different domains on the same host, managing them using different ports can be challenging. With the use of a reverse proxy like Nginx, combined with the advantages of running containers with Podman, you can create a multidomain architecture in a simple way.

For more sophisticated scaling, orchestration, routing, and balancing of many container-based applications and services, you can use a Kubernetes-based enterprise orchestration platform such as the Red Hat OpenShift Container Platform.

Stay tuned for more articles on related topics! In my next article, I'll show how to use Ansible to automate Podman for container and pod deployments using the environment from this article as an example.

[ Learn how to set up Nginx on OpenShift and AWS ROSA. ]

Topics:   Podman   Containers  
Author’s photo

Alexon Oliveira

Alexon has been working as a Senior Technical Account Manager at Red Hat since 2018, working in the Customer Success organization focusing on Infrastructure and Management, Integration and Automation, Cloud Computing, and Storage Solutions. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.