I'm trying to create a Docker container that acts like a full-on virtual machine. I know I can use the EXPOSE instruction inside a Dockerfile to expose a port, and I can use the -p flag with docker run to assign ports, but once a container is actually running, is there a command to open/map additional ports live?
For example, let's say I have a Docker container that is running sshd. Someone else using the container ssh's in and installs httpd. Is there a way to expose port 80 on the container and map it to port 8080 on the host, so that people can visit the web server running in the container, without restarting it?
You cannot do this via Docker, but you can access the container's un-exposed port from the host machine.
If you have a container with something running on its port 8000, you can run
wget http://container_ip:8000
To get the container's IP address, run the 2 commands:
docker ps
docker inspect container_name | grep IPAddress
Internally, Docker shells out to call iptables when you run an image, so maybe some variation on this will work.
To expose the container's port 8000 on your localhost's port 8001:
iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000
One way you can work this out is to setup another container with the port mapping you want, and compare the output of the iptables-save command (though, I had to remove some of the other options that force traffic to go via the docker proxy).
NOTE: this is subverting docker, so should be done with the awareness that it may well create blue smoke.
OR
Another alternative is to look at the (new? post 0.6.6?) -P option - which will use random host ports, and then wire those up.
OR
With 0.6.5, you could use the LINKs feature to bring up a new container that talks to the existing one, with some additional relaying to that container's -p flags? (I have not used LINKs yet.)
OR
With docker 0.11? you can use docker run --net host .. to attach your container directly to the host's network interfaces (i.e., net is not namespaced) and thus all ports you open in the container are exposed.
Here's what I would do:
Commit the live container.
Run the container again with the new image, with ports open (I'd recommend mounting a shared volume and opening the ssh port as well)
sudo docker ps
sudo docker commit <containerid> <foo/live>
sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash
While you cannot expose a new port of an existing container, you can start a new container in the same Docker network and get it to forward traffic to the original container.
# docker run \
--rm \
-p $PORT:1234 \
verb/socat \
TCP-LISTEN:1234,fork \
TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT
Worked Example
Launch a web-service that listens on port 80, but do not expose its internal port 80 (oops!):
# docker run -ti mkodockx/docker-pastebin # Forgot to expose PORT 80!
Find its Docker network IP:
# docker inspect 63256f72142a | grep IPAddress
"IPAddress": "172.17.0.2",
Launch verb/socat with port 8080 exposed, and get it to forward TCP traffic to that IP's port 80:
# docker run --rm -p 8080:1234 verb/socat TCP-LISTEN:1234,fork TCP-CONNECT:172.17.0.2:80
You can now access pastebin on http://localhost:8080/, and your requests goes to socat:1234 which forwards it to pastebin:80, and the response travels the same path in reverse.
IPtables hacks don't work, at least on Docker 1.4.1.
The best way would be to run another container with the exposed port and relay with socat. This is what I've done to (temporarily) connect to the database with SQLPlus:
docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus
Dockerfile:
FROM debian:7
RUN apt-get update && \
apt-get -y install socat && \
apt-get clean
USER nobody
CMD socat -dddd TCP-LISTEN:1521,reuseaddr,fork TCP:db:1521
Here's another idea. Use SSH to do the port forwarding; this has the benefit of also working in OS X (and probably Windows) when your Docker host is a VM.
docker exec -it <containterid> ssh -R5432:localhost:5432 <user>#<hostip>
To add to the accepted answer iptables solution, I had to run two more commands on the host to open it to the outside world.
HOST> iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.17.0.2:443
HOST> iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source 172.17.0.2 --destination 172.17.0.2 --dport https
HOST> iptables -A DOCKER -j ACCEPT -p tcp --destination 172.17.0.2 --dport https
Note: I was opening port https (443), my docker internal IP was 172.17.0.2
Note 2: These rules and temporrary and will only last until the container is restarted
I had to deal with this same issue and was able to solve it without stopping any of my running containers. This is a solution up-to-date as of February 2016, using Docker 1.9.1. Anyway, this answer is a detailed version of #ricardo-branco's answer, but in more depth for new users.
In my scenario, I wanted to temporarily connect to MySQL running in a container, and since other application containers are linked to it, stopping, reconfiguring, and re-running the database container was a non-starter.
Since I'd like to access the MySQL database externally (from Sequel Pro via SSH tunneling), I'm going to use port 33306 on the host machine. (Not 3306, just in case there is an outer MySQL instance running.)
About an hour of tweaking iptables proved fruitless, even though:
Step by step, here's what I did:
mkdir db-expose-33306
cd db-expose-33306
vim Dockerfile
Edit dockerfile, placing this inside:
# Exposes port 3306 on linked "db" container, to be accessible at host:33306
FROM ubuntu:latest # (Recommended to use the same base as the DB container)
RUN apt-get update && \
apt-get -y install socat && \
apt-get clean
USER nobody
EXPOSE 33306
CMD socat -dddd TCP-LISTEN:33306,reuseaddr,fork TCP:db:3306
Then build the image:
docker build -t your-namespace/db-expose-33306 .
Then run it, linking to your running container. (Use -d instead of -rm to keep it in the background until explicitly stopped and removed. I only want it running temporarily in this case.)
docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306 your-namespace/db-expose-33306
You can use SSH to create a tunnel and expose your container in your host.
You can do it in both ways, from container to host and from host to container. But you need a SSH tool like OpenSSH in both (client in one and server in another).
For example, in the container, you can do
$ yum install -y openssh openssh-server.x86_64
service sshd restart
Stopping sshd: [FAILED]
Generating SSH2 RSA host key: [ OK ]
Generating SSH1 RSA host key: [ OK ]
Generating SSH2 DSA host key: [ OK ]
Starting sshd: [ OK ]
$ passwd # You need to set a root password..
You can find the container IP address from this line (in the container):
$ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*/\1/g'
172.17.0.2
Then in the host, you can just do:
sudo ssh -NfL 80:0.0.0.0:80 root#172.17.0.2
Based on Robm's answer I have created a Docker image and a Bash script called portcat.
Using portcat, you can easily map multiple ports to an existing Docker container. An example using the (optional) Bash script:
curl -sL https://raw.githubusercontent.com/archan937/portcat/master/script/install | sudo bash
portcat my-awesome-container 3456 4444:8080
And there you go! Portcat is mapping:
port 3456 to my-awesome-container:3456
port 4444 to my-awesome-container:8080
Please note that the Bash script is optional, the following commands:
ipAddress=$(docker inspect my-awesome-container | grep IPAddress | grep -o '[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}' | head -n 1)
docker run -p 3456:3456 -p 4444:4444 --name=alpine-portcat -it pmelegend/portcat:latest $ipAddress 3456 4444:8080
I hope portcat will come in handy for you guys. Cheers!
There is a handy HAProxy wrapper.
docker run -it -p LOCALPORT:PROXYPORT --rm --link TARGET_CONTAINER:EZNAME -e "BACKEND_HOST=EZNAME" -e "BACKEND_PORT=PROXYPORT" demandbase/docker-tcp-proxy
This creates an HAProxy to the target container. easy peasy.
Here are some solutions:
https://forums.docker.com/t/how-to-expose-port-on-running-container/3252/12
The solution to mapping port while running the container.
docker run -d --net=host myvnc
that will expose and map the port automatically to your host
In case no answer is working for someone - check if your target container is already running in docker network:
CONTAINER=my-target-container
docker inspect $CONTAINER | grep NetworkMode
"NetworkMode": "my-network-name",
Save it for later in the variable $NET_NAME:
NET_NAME=$(docker inspect --format '{{.HostConfig.NetworkMode}}' $CONTAINER)
If yes, you should run the proxy container in the same network.
Next look up the alias for the container:
docker inspect $CONTAINER | grep -A2 Aliases
"Aliases": [
"my-alias",
"23ea4ea42e34a"
Save it for later in the variable $ALIAS:
ALIAS=$(docker inspect --format '{{index .NetworkSettings.Networks "'$NET_NAME'" "Aliases" 0}}' $CONTAINER)
Now run socat in a container in the network $NET_NAME to bridge to the $ALIASed container's exposed (but not published) port:
docker run \
--detach --name my-new-proxy \
--net $NET_NAME \
--publish 8080:1234 \
alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:$ALIAS:80
You can use an overlay network like Weave Net, which will assign a unique IP address to each container and implicitly expose all the ports to every container part of the network.
Weave also provides host network integration. It is disabled by default but, if you want to also access the container IP addresses (and all its ports) from the host, you can run simply run weave expose.
Full disclosure: I work at Weaveworks.
It's not possible to do live port mapping but there are multiple ways you can give a Docker container what amounts to a real interface like a virtual machine would have.
Macvlan Interfaces
Docker now includes a Macvlan network driver. This attaches a Docker network to a "real world" interface and allows you to assign that networks addresses directly to the container (like a virtual machines bridged mode).
docker network create \
-d macvlan \
--subnet=172.16.86.0/24 \
--gateway=172.16.86.1 \
-o parent=eth0 pub_net
pipework can also map a real interface into a container or setup a sub interface in older versions of Docker.
Routing IP's
If you have control of the network you can route additional networks to your Docker host for use in the containers.
Then you assign that network to the containers and setup your Docker host to route the packets via the docker network.
Shared host interface
The --net host option allows the host interface to be shared into a container but this is probably not a good setup for running multiple containers on the one host due to the shared nature.
Read Ricardo's response first. This worked for me.
However, there exists a scenario where this won't work if the running container was kicked off using docker-compose. This is because docker-compose (I'm running docker 1.17) creates a new network. The way to address this scenario would be
docker network ls
Then append the following
docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name
docker run -i --expose=22 b5593e60c33b bash
ref: https://forums.docker.com/t/how-to-expose-port-on-running-container/3252/5
this may help you
Related
I'm not sure if I've misunderstood something here, but it seems like it's only possible to set port mappings by creating a new container from an image. Is there a way to assign a port mapping to an existing Docker container?
I'm also interested in this problem.
As #Thasmo mentioned, port forwardings can be specified ONLY with docker run (and docker create) command.
Other commands, docker start does not have -p option and docker port only displays current forwardings.
To add port forwardings, I always follow these steps,
stop running container
docker stop test01
commit the container
docker commit test01 test02
NOTE: The above, test02 is a new image that I'm constructing from the test01 container.
re-run from the commited image
docker run -p 8080:8080 -td test02
Where the first 8080 is the local port and the second 8080 is the container port.
You can change the port mapping by directly editing the hostconfig.json file at
/var/lib/docker/containers/[hash_of_the_container]/hostconfig.json or /var/snap/docker/common/var-lib-docker/containers/[hash_of_the_container]/hostconfig.json, I believe, if You installed Docker as a snap.
You can determine the [hash_of_the_container] via the docker inspect <container_name> command and the value of the "Id" field is the hash.
Stop the container (docker stop <container_name>).
Stop docker service (per Tacsiazuma's comment)
Change the file.
Restart your docker engine (to flush/clear config caches).
Start the container (docker start <container_name>).
So you don't need to create an image with this approach. You can also change the restart flag here.
P.S. You may visit https://docs.docker.com/engine/admin/ to learn how to correctly restart your docker engine as per your host machine. I used sudo systemctl restart docker to restart my docker engine that is running on Ubuntu 16.04.
If by "existing" you mean "running", then it's not (currently) possible to add a port mapping.
You can, however, dynamically add a new network interface with e.g. Pipework, if you need to expose a service in a running container without stopping/restarting it.
Editing hostconfig.json seems to not working now. It only ends with that port being exposed but not published to host. Commiting and recreating containers is not the best approach to me. No one mentioned docker network?
The best solution would be using reversed proxy within the same network
Create a new network if your previous container not in any named ones.
docker network create my_network
Join your existing container to the created network
docker network connect my_network my_existing_container
Start a reversed proxy service(e.g. nginx) publishing the ports you need, joining the same network
docker run -d --name nginx --network my_network -p 9000:9000 nginx
Optionally remove the default.conf in nginx
docker exec nginx rm /etc/nginx/conf.d/default.conf
Create a new nginx config
server
{
listen 9000;
location / {
proxy_pass http://my_existing_container:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Copy the config to nginx container.
docker cp ./my_conf.conf nginx:/etc/nginx/conf.d/my_conf.conf
Restart nginx
docker restart nginx
Advantages: To publish new ports, you can safely stop/update/recreate nginx container as you wish without touching the business container. If you need zero down time for nginx, it is possible to add more reversed proxy services joining the same network. Besides, a container can join more than one network.
Edit:
To reverse proxy non-http services, the config file is a bit different. Here is a simple example:
upstream my_service {
server my_existing_container:9000;
}
server {
listen 9000;
proxy_pass my_service;
}
In Fujimoto Youichi's example test01 is a container, whereas test02 is an image.
Before doing docker run you can remove the original container and then assign the container the same name again:
$ docker stop container01
$ docker commit container01 image01
$ docker rm container01
$ docker run -d -P --name container01 image01
(Using -P to expose ports to random ports rather than manually assigning).
If you are not comfortable with Docker depth configuration, iptables would be your friend.
iptables -t nat -A DOCKER -p tcp --dport ${YOURPORT} -j DNAT --to-destination ${CONTAINERIP}:${YOURPORT}
iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source ${CONTAINERIP} --destination ${CONTAINERIP} --dport ${YOURPORT}
iptables -A DOCKER -j ACCEPT -p tcp --destination ${CONTAINERIP} --dport ${YOURPORT}
This is just a trick, not a recommended way. This works with my scenario because I could not stop the container.
If you run docker run <NAME> it will spawn a new image, which most likely isn't what you want.
If you want to change a current image do the following:
docker ps -a
Take the id of your target container and go to:
cd /var/lib/docker/containers/<conainerID><and then some:)>
Stop the container:
docker stop <NAME>
Change the files
vi config.v2.json
"Config": {
....
"ExposedPorts": {
"80/tcp": {},
"8888/tcp": {}
},
....
},
"NetworkSettings": {
....
"Ports": {
"80/tcp": [
{
"HostIp": "",
"HostPort": "80"
}
],
And change file
vi hostconfig.json
"PortBindings": {
"80/tcp": [
{
"HostIp": "",
"HostPort": "80"
}
],
"8888/tcp": [
{
"HostIp": "",
"HostPort": "8888"
}
]
}
Restart your docker and it should work.
Not sure if you can apply port mapping a running container. You can apply port forwarding while running a container which is different than creating a new container.
$ docker run -p <public_port>:<private_port> -d <image>
will start running container. This tutorial explains port redirection.
To change HostPort of a container on Docker Desktop (on Windows 10 / MacOS)
# list all containers
$ docker ps -a
$ docker stop docker101tutorial
# Use grep to get id of container
$ docker inspect docker101tutorial | grep -i id
"Id": "sha256:fff0a4b22d6f3d2eb8d2748b8a8bbc9967ea87199988acee8e86ac70bce9c3eb",
# run plain ubuntu docker image with shell and change it's namespace to docker host
# https://stackoverflow.com/questions/60408574/how-to-access-var-lib-docker-in-windows-10-docker-desktop/60411313#60411313
# https://forums.docker.com/t/the-location-of-images-in-docker-for-windows/19647/4
$ docker run -it --privileged --pid=host ubuntu nsenter -t 1 -m -u -i sh
# We want to find out the directory of docker101tutorial container. We are looking for:
# `"Image":"sha256:fff0a4b22d6f3d2eb8d2748b8a8bbc9967ea87199988acee8e86ac70bce9c3eb"`
# in /var/lib/docker/containers/*/config.v2.json
$ find /var/lib/docker/containers/ -name config.v2.json -exec grep -H fff0a4b22d {} \;
/var/lib/docker/containers/c1eda20b30f058bce9f8ece3b47a21641df5b399770e12ab57416a954d3c8bbf/config.v2.json
# edit it
$ vi /var/lib/docker/containers/c1eda20b30f058bce9f8ece3b47a21641df5b399770e12ab57416a954d3c8bbf/hostconfig.json
Press i for insert mode.
Change "HostPort":"80" to "HostPort":"8092"
Press Escape and write :wq. Press Enter.
Do not start/stop docker101tutorial now. Otherwise changes to HostPort will be reverted.
Right click Docker Desktop tray icon and click Restart.
In Docker Desktop's list of containers, look at your container. Displayed port should change to 8092.
Start your container. Now it will be mapped to port 8092 on host.
Based on #holdfenytolvaj answer.
we an use handy tools like ssh to accomplish this easily.
I was using ubuntu host and ubuntu based docker image.
Inside docker have openssh-client installed.
Outside docker (host) have openssh-server server installed.
when a new port is needed to be mapped out,
inside the docker run the following command
ssh -R8888:localhost:8888 <username>#172.17.0.1
172.17.0.1 was the ip of the docker interface
(you can get this by running
ifconfig docker0 | grep "inet addr" | cut -f2 -d":" | cut -f1 -d" " on the host).
here I had local 8888 port mapped back to the hosts 8888. you can change the port as needed.
if you need one more port, you can kill the ssh and add one more line of -R to it with the new port.
I have tested this with netcat.
Stop the docker engine and that container.
Go to /var/lib/docker/containers/${container_id} directory and edit hostconfig.json
Edit PortBindings.HostPort that you want the change.
Start docker engine and container.
PORTAINER METHOD
If anyone is using portainer, I found this solution to be "Gui friendly and safe"
Go to the container in Portainer
Stop the container
Select "Duplicate/Edit"
Add the needed ports and deploy.
When asked if replace or cancel, select replace.
Enjoy life
I had this situation with my container:
127.0.0.1:8050->8000
And I solved it with one iptalbes rule:
iptables -t nat -I DOCKER 1 -p tcp --dport ${HOST_PORT} -j DNAT --to-destination ${CONTAINER_IP}:${CONTAINER_PORT}
In this example HOST_PORT is 8050 and CONTAINER_PORT is 8000. To find CONTAINER_IP use docker inspect command.
To remove the iptables rule use this command:
iptables -t nat -D DOCKER 1
For Windows & Mac Users, there is another pretty easy and friendly way to change the mapping port:
download kitematic
go to the settings page of the container, on the ports tab, you can directly modify the published port there.
start the container again
How do I assign a port mapping to an existing Docker container ?
It's very Simple. There is two thing one is local server PORT like 800,8080 etc. on your machine and the second one is your container PORT which you want to map. Docker Port mapping
docker run -p 8080:8080 <Image ID>
To get Image Id you can use
docker ps
I have 2 containers, first one running on port 80 and the other is running on port 8022, I try to make a curl request from the container that`s is running on port 8022 to the container on port 80 and i get a empty response.
For the container on port 8022 i run this command:
docker run -d -it --privileged -p 0.0.0.0:8022:80 -v ~/path/to/my/app:/var/www/app --network=bridge --memory 1073741824 my/app:latest
If a make a curl request to other host form example google, I get the response correctly.
Thanks for help
UPDATED
Ok I can solve this, creating a network and using this network in both containers, then I add to host file of the 8022 container the IP of the port 80 container.
Thanks for #zero298 for help!!
This is a very simple example of how to do inter-container communication over a user-defined, bridged network. Here are the 3 files that I have defined to make this possible:
~/Desktop/code/bootstrap.sh
This will kick off the demo by first creating a user defined, isolated network that your containers can talk over named example_nw. It then creates a new container, named "servertest", that will hold the server we will curl to. I'm using a node container because I'm just more familiar with it.
It will also create a volume that binds to your machines ~/Desktop/code/ directory which should contain all the code that we are using, including the node server.js script. The server listens on port 3000 and responds with: "Hello World`".
After creating the server, it kicks off another container, named "curler" that will install curl (debian doesn't come with it installed). After that, it curls to servertest:3000 and gets the correct reply because they are both connected to the same docker, user-defined, network: example_nw.
After completing, it cleans up by killing the server container and removing the example_nw network.
#!/usr/bin/env bash
# Create user-defined network
docker network create example_nw
# Create server to listen to pings on our network
docker run \
--rm \
-d \
-v ~/Desktop/code:/my/stuff \
--name servertest \
--expose 3000 \
--network example_nw \
node node /my/stuff/server
# Create a curler
docker run \
-it \
--rm \
-v ~/Desktop/code:/my/stuff \
--name pingtest \
--network example_nw debian \
/my/stuff/curler.sh
# Clean up
docker container stop servertest
docker network rm example_nw
~/Desktop/code/server.js
This is a really simple node.js script that will create a server that listens on port 3000.
/*jslint node:true, esversion:6*/
"use strict";
const http = require("http"),
port = 3000;
// Make a server
const server = http.createServer((req, res) => {
console.log("Got request");
res.end("Hello World!\n");
});
// Have the server listen
server.listen(port, (err) => {
if (err) {
return console.error(err);
}
console.log(`Listening on port: ${port}`);
});
~/Desktop/code/curler.sh
This just installs curl in the container and then curls to servertest:3000.
#!/usr/bin/env bash
apt update
apt install -y curl
curl servertest:3000
Running ~/Desktop/code/bootstrap.sh will demonstrate the communication.
I would recommend reading the Docker documentation: Work with network commands because it gives a lot of good examples and use cases.
This is an example to use the user-generated network
docker network create mynetwork
docker run --network=mynetwork -d -p 127.0.0.1:8022:80 --name mynginx nginx
docker run -it --network=mynetwork appropriate/curl http://mynginx
Otherwise you can use the older --link option
docker run -d -p 127.0.0.1:8022:80 --name mynginx nginx
docker run -it --link=nginx appropriate/curl http://mynginx
Your container is not listening on port 8022, you've pushed the port 8022 to forward to the container's port 80. From container to container, you connect to the container port, not the published port on the host. So remove 8022 from your curl command to go to the default port 80.
For container to container networking, you need the containers to be on a common docker network. And for docker's internal DNS, you need to be on a docker network other than the default bridge network named "bridge" (that "bridge" network has some historical properties that does not have DNS enabled). You can docker network create $network_name and then run your containers with the --net $network_name option to implement this.
You do not need to modify the host files or use the deprecated "link" functionality for container to container networking.
To make development easier for a project, I've put a couple of services it depends on in docker containers. This makes 'localhost' in the project's config mean something different when it is passed to one of the containers.
edit
To be clear, I'm trying to forward one of the container's ports to the host so when a process running in the container tries to access localhost:5432, it connects to the host's port 5432.
endedit
I'm currently using
HOST_IP=`ip route | grep default | awk '{ printf "%s",$3 }'`
cat /etc/hosts | sed "s/127.0.0.1/$HOST_IP/" > /tmp/etc_hosts
cp /tmp/etc_hosts /etc/hosts
to redirect anything targeting 'localhost' to the container's host. It works in this situation, but I'd prefer to find a way to do this only for the needed port as I expect it won't work in other situations.
Here's what I came up with to do that, but it's not working; when a connection in the container is to localhost:5432, it tries to connect to the container's 5432 instead of the host's:
# --- These are the things that should make redirecting port 5432 to the host machine
# work, provided the container is run in privileged mode.
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.route_localnet=1
iptables -t nat -A PREROUTING -p tcp --dport 5432 -j DNAT --to 172.19.0.1:5432
iptables -A FORWARD -d 172.19.0.1 -p tcp --dport 5432 -j ACCEPT
iptables -t nat -A POSTROUTING -j MASQUERADE
If I understand well, for development, you'd want localhost to resolve to a specific container, including when it's called from another container.
Host forwarding
Rewriting your hosts file is, as you mentioned it, not a good idea, since many services can experiment issues if you design localhost as being something different than, well... your local host.
But you can consider a few solutions.
Docker Toolbox
If running docker with Docker Toolbox, or by yourself on a virtual machine with Virtual Box, the intermediate virtual machine is visible, so localhost will represent it. You'll have to run the container, exposing this port, and then to set up a port forwarding in Virtualbox. If I use Wordpress as an example:
docker run -p 80:80 --name website -d wordpress
Virtual Box -> your docker VM (usually called default) -> Network -> Adapter 1 -> port forwarding -> create a mapping from host 8080 to guest 80
It will make Wordpress available at http://localhost:8080. Please note that under MacOS, the kernel restrains non-privileged port forwarding (ports under 1024).
This port forwarding can be created in command line, if you want to put it in a script:
VBoxManage modifyvm "default" --natpf1 "app,tcp,,8080,,80"
Docker for Windows/Docker for Mac
If running docker through Docker for Windows/Docker for Mac (or directly under Linux), rather than Docker Toolbox, you can run the container using the -p parameter, as specified by Scott's post, and your service will be available on localhost at this port (because the intermediate virtual machine is transparent, or no VM under Linux):
docker run -p 5432:5432 --name myapp -d myimage will make myapp available at localhost:5432.
socat (or iptables)
You can run socat on your host this way to forward communication on a specific port to your container:
socat TCP-LISTEN:5432,fork,reuseaddr,user=node,group=node,mode=777 TCP:172.19.0.1:5432 &
(where 172.19.0.1 is your container IP)
Container forwarding
--network
Your containers have their own hosts file, that you can see by issuing such a command:
docker run ubuntu cat /etc/hosts
You can add entries to hosts with the --add-host parameter:
docker run --add-host domain:1.2.3.4 --add-host domain2:5.6.7.8 ubuntu cat /etc/hosts
However this solution will be useless for localhost, because it won't remove the previous localhost associations. What you're looking for (and what is cleaner) is the parameter --network=host which allows the container to share the network interfaces of the host:
docker run --network=host ubuntu
This way, your container will be able to call the other containers services on localhost using their port.
The right way
Of course, the right way to achieve what you want would be to link your containers together and use their link names rather than localhost.
docker run -d --name mariadb -e MYSQL_ROOT_PASSWORD=password mariadb
docker run -d --name="wordpress" -p 8080:80 -e WORDPRESS_DB_PASSWORD=password --link mariadb:mysql wordpress
In this case, the Wordpress container will have a mysql entry in its hosts file, pointing to the mariadb container IP address. To see it, open a bash session in the Wordpress container and see by yourself.
docker exec -ti wordpress bash
#cat /etc/hosts
Show us how you are launching your container
port mapping can happen in your docker run command : -p hostport:containerport
as in
docker run -p 5432:5432 --name mycontainer -d myimage
Suppose I have the following container:
docker run -i -t test/python3 /bin/bash
and run the following in the shell:
python3 -m http.server 8080
How do I connect to this port from the host? Is this possible?
You can connect directly to the container using its private IP. To discover it, in the host terminal you can run docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container_id>. The container_id can be obtained using docker ps once the container is running (also from the host). Once you have that ip you can access directly to it to the port you wish.
Also you have the option to publish a container port to the host machine, thus you can access directly from you localmachine to localhost:8080, or from any other machine that has visibility with the host (to the adequate ip or hostname). To publish the port you have to use the option -p, as describer in the docker run prompt help:
$ docker run --help
(...)
-p, --publish=[] Publish a container's port to the host
format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
(...)
So in that case you could run docker run -i -t -p 8080:8080 test/python3 /bin/bash to, for example, publish the port 8080 of the container to the same number port of your local machine (you could choose any other not already in use in your host machine). Also may be you don't need to run the container in a interactive way if you don't need to perform any further action inside (or doing this using a script), and running directly the command you wish to execute: docker run -i -t -p 8080:8080 test/python3 python3 -m http.server 8080
How do I forward a UDP port from my Docker container to the host machine?
Use the -p flag and add /udp suffix to the port number.
-p 53160:53160/udp
Full command
sudo docker run -p 53160:53160 \
-p 53160:53160/udp -p 58846:58846 \
-p 8112:8112 -t -i aostanin/deluge /start.sh
If you're running boot2docker on Mac, be sure to forward the same ports on boot2docker to your local machine.
You can also document that your container needs to receive UDP using EXPOSE in The Dockerfile (EXPOSE does not publish the port):
EXPOSE 8285/udp
Here is a link with more Docker Networking info covered in the container docs:
https://docs.docker.com/config/containers/container-networking/
(Courtesy of Old Pro in the comments)
Just thought I would pitch in for docker-compose config.
ports:
- "9955:9955/udp"