How to create multidomain web applications with Podman and Nginx
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:
And here's what the sysadmin.org application looks like:
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. ]
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