This is more of a guide for doing reverse proxying via nginx for a TCP server on port 80 using Nginx. I am writing this because when I was trying to figure out how to do this, all I had was Nginx’s TCP Load Balancing article (which ended up being enough), but just having a how-to like would’ve been nice.

Before you get started

A couple of things to note:

  • You must be using Nginx version 1.9 or higher. Currently you can get 1.9 through the mainline branch.
  • You cannot be running HTTP servers on the same machine over port 80. Running the TCP server on port 80 is achieved easily by removing the HTTP section of the nginx config.

Other than that, you need to make sure to get rid of the top-level http directive within your nginx.conf. This can usually be found at /etc/nginx/nginx.conf. If you still have the http directive, it will block this from working. I would recommend replacing your nginx.conf file with something similar to what is below.

The Config

#
# This config file is based off the default one created by the "mainline" install.
#
# The main difference is the removal of the `http` section as we are only using this nginx install
# for TCP proxying.
#

#
# From Default Config
#
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}
#
# End of Default Config
#


#
# Everything below this is "custom", and does not come from the default mainline config file.
#


# Guide for TCP: https://www.nginx.com/resources/admin-guide/tcp-load-balancing/
#
# This is the config needed for:
#   A. Routing TCP traffic via port 80
#   B. Doing TCP Load Balancing
#
# This config is a little different from the others because it cannot live in "sites-enabled", because
# it cannot be stored under the `http` directive in /etc/nginx/nginx.conf
#
# Also, since server_name is not allowed, when its on Port 80, this must be the only thing running on Port 80.
# What this means is that you can not ALSO server HTTP sites on whatever server this is running on.
stream {
    server {
        # Listens on port 80
        listen 80;

        #
        # MAKE SURE TO CHANGE THE BELOW TO THE ACTUAL MACHINE
        #

        # Proxies all requests to the tcp server running on port 1234 on whatever
        # IP example.com points to.
        # proxy_pass example.com:1234;

        # Proxies all requests to local host at port 1234.
        proxy_pass 127.0.0.1:1234;
    }
}

As shown within the above config, this can easily be changed to provide load balancing. All you have to do is change the stream directive to something like this:

    stream {
        upstream tcp_servers {
            server example.com:1234;
            server backup.example.com:2345;
        }

        server {
            listen 80;
            proxy_pass tcp_servers;
        }
    }