Since you need to route based on hostname instead of port, the iptables solution of NAT'ing the traffic to the VMs is out.
What you're left with is running a web server in reverse proxy mode, that reads the request host header and proxies to the different private IPs based on the request's header.
Exact configuration will depend on which web server you use, and which web server you select will depend on what features you need (SSL?) as well as personal preference. Let me know which web server you'd prefer and I can edit the answer to include an example config, if needed.
edit: Basic nginx config:
http {
# ...existing config basics... server_name, NOT servername
server {
listen 80;
server_name git.example.com;
location / {
# git server IP below:
proxy_pass http://10.x.x.x:80/;
# re-send the host header - this may not be necessary
proxy_set_header Host $host;
# set the X-Forwarded-For header, so that the public IP of the client is available to the backend server
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
server_name psql.example.com;
location / {
# psql server IP below:
proxy_pass http://10.x.x.x:80/;
# re-send the host header - this may not be necessary
proxy_set_header Host $host;
# set the X-Forwarded-For header, so that the public IP of the client is available to the backend server
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
server_name www.example.com;
location / {
# www server IP below:
proxy_pass http://10.x.x.x:80/;
# re-send the host header - this may not be necessary
proxy_set_header Host $host;
# set the X-Forwarded-For header, so that the public IP of the client is available to the backend server
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}