Connection variables: ansible_host, ansible_port, ansible_user and ansible_connection, with ssh, docker and local
Fourth entry in the Ansible inventory from scratch series. The inventory so far, from item 1 to item 3, named hosts and groups and connected to all of them on the local machine. This entry reaches three hosts three different ways, and looks at the four variables that decide how: ansible_connection, ansible_host, ansible_port and ansible_user. Everything below ran with ansible-core 2.21.4 and community.docker 5.3.0.
Three hosts, three connection plugins
A connection plugin is the part of Ansible that carries tasks to a host and runs them there. ansible_connection picks it, per host, and defaults to ssh:
sshlogs in over SSH, the usual way to reach a server.community.docker.docker, from the community.docker collection (a package of extra plugins and modules, installed withansible-galaxy), runs commands inside a container, an isolated process with its own filesystem, through Docker, with no SSH at all.localruns on the controller, the machine runningansible-playbook.
The example gave each host its settings in host_vars/<host>/ansible.yml, item 2's layout for one host, and kept the hosts file free of variables:
# host_vars/db1/ansible.yml
ansible_connection: ssh
ansible_host: 127.0.0.1
ansible_port: 2222
ansible_user: dbadmin
# host_vars/app1/ansible.yml
ansible_connection: community.docker.docker
ansible_host: inv04-app1
ansible_user: appuser
# host_vars/jump1/ansible.yml
ansible_connection: local
db1 was an SSH server in a container, published on the controller's port 2222; app1 a second container, named inv04-app1. A playbook gathered, from inside each host, the user it ran as and the machine's name:
app1: community.docker.docker → appuser on app1-container
db1: ssh → dbadmin on db1-container
jump1: local → the controller's own user, on the controller
The same variable means something different per plugin
Each connection plugin maps these variables to options of its own, and its documentation says what they mean:
| Variable | ssh |
community.docker.docker |
local |
|---|---|---|---|
ansible_host |
the name or address to connect to | the container's name | not used |
ansible_port |
the SSH port | not used | not used |
ansible_user |
the user to log in as | the user to run as inside the container | ignored |
ansible_hostdefaults to the host's inventory name in bothsshanddocker. The name in the hosts file is a label;ansible_hostis where the connection goes, so the inventory can saydb1while SSH connects to127.0.0.1.ansible_porthas no default in Ansible. Unset, thesshplugin passes no port, and SSH uses its own default, 22, or what~/.ssh/configsays for that host.localignoresansible_user. Its documentation says so: "The remote user is ignored, the user with which the ansible CLI was executed is used instead." With-e ansible_user=nobody,id -unon jump1 still printed the controller's user. Running a task as another user is the job ofbecome, Ansible's switch to another user once connected (with sudo or su), not the connection's.
The older names, ansible_ssh_host, ansible_ssh_port and ansible_ssh_user, still work with ssh, and ansible_docker_host and ansible_docker_user with docker; the plugins' documentation lists them next to the generic ones.
When ansible_host is missing
The example left ansible_host at its default, the inventory name, for each container host:
- db1 over SSH failed clearly: "Could not resolve hostname db1", followed by the resolver's own reason, which differed between machines ("Name or service not known" locally, "Temporary failure in name resolution" on GitHub's runner), and the host was marked unreachable.
- app1 through Docker failed misleadingly: "Failed to create temporary directory. In some cases, you may have been able to authenticate and did not have permissions on the target directory." There was no container called
app1, and nothing said so; even at-vvv, the only clue was the command Ansible ran,docker exec -u appuser -i app1 …, with the container name it tried.
So when a docker host is unreachable with a permissions message, check ansible_host against docker ps first.
In short
ansible_connectionpicks the plugin per host;sshif unset.ansible_hostis where to connect, the inventory name if unset; for docker, a container name.ansible_portandansible_useronly mean something to plugins that use them;localignores both.- Put them in
host_vars/<host>/ansible.yml, or ingroup_vars/<group>/ansible.ymlwhen a whole group connects the same way.
The example repository
The series' companion repository, abdelhousni/ansible-inventory-series, holds this inventory and the image behind db1 and app1. run.sh needs Docker and an SSH client: it starts both containers, generates a key pair for the run, runs whoami.yml, which writes where each connection landed to its out/ directory, then repeats the three mistakes above. Its CI runs it on every push and compares the output with the expected one.
Sources
- ansible-core 2.21.4: the documentation of
plugins/connection/ssh.py(host,port,remote_userand their variables) andplugins/connection/local.py(the ignored remote user). - community.docker 5.3.0: the documentation of
plugins/connection/docker.py(remote_addr, the container's name, andremote_user). - Every result above came from ansible-core 2.21.4 and community.docker 5.3.0 on 2026-10-03, with Docker 29.6.2 and containers from
python:3.12-slim, on the local machine.
Created 2026-10-03T08:56:48+02:00 · Edit