# \`ansible\_host\` with \`delegate\_to\` points to \`inventory\_hostname\`, not delegated host

**URL:** <https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075>\
**Category:** Get Help\
**Tags:** template, ansible-core\
**Created:** [January 9, 2026, 11:29am UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075 "2026-01-09T11:29:18Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![marvic](https://avatars.discourse-cdn.com/v4/letter/m/45deac/32.png) [@marvic](https://forum.ansible.com/u/marvic)\
**Post date:** [January 9, 2026, 11:29am UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/1 "2026-01-09T11:29:18Z")

</div>

Hello,  
I am quite new to Ansible, hence knowledge might be a bit rough on the edges. As follows is a simple reproduction example.

inventory.yml:

```yaml
# Inventory has inlined variables for ease of use.
# In practice these are defined within host_vars.
all:
  hosts:
    localhost:
      ansible_host: 127.0.0.1
      ansible_connection: local
    myhost:
      ansible_host: 192.168.100.186

```

main.yml:

```yaml
- name: Example play
  gather_facts: false
  hosts: myhost
  tasks:

    - ansible.builtin.shell:
        cmd: >-
          echo "Delegated from {{ inventory_hostname }} to {{ ansible_host }},"
          "target hostname is $(hostnamectl hostname)"
      delegate_to: localhost
      register: result

    - ansible.builtin.debug:
        msg: "{{ result.stdout }}"

```

```bash
$ ansible-playbook -i inventory.yml main.yml
 
PLAY [Example play] ************************************************************************************************************************************************************************
 
TASK [ansible.builtin.shell] ***************************************************************************************************************************************************************
changed: [myhost -> localhost(127.0.0.1)]
 
TASK [ansible.builtin.debug] ***************************************************************************************************************************************************************
ok: [myhost] => {
    "msg": "Delegated from myhost to 192.168.100.186, target hostname is test-ansible-controller"     
}

```

Expected: `{{ ansible_host }}` is 127.0.0.1.  
Actual: `{{ ansible_host }}` is 192.168.100.186 , i.e. `inventory_hostname`.

Having read [https://docs.ansible.com/projects/ansible/latest/playbook\_guide/playbooks\_delegation.html#templating-in-delegation-context](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_delegation.html) , this was surprising to me:

> The ansible\_host variable and other connection variables, if present, reflects information about the host a task is delegated to, not the inventory\_hostname.
> 
> All variables except inventory\_hostname will now be consumed from this host and not the original task host.

I also encountered [ansible\_host is incorrect when using delegate\_to · Issue #14958 · ansible/ansible · GitHub](https://github.com/ansible/ansible/issues/14958#issuecomment-292019847) (old):

> So the confusion here is not about connection but display, for templating ansible\_host will reflect inventory\_hostname.

, as well as [Variables not consumed from delegated host - #2 by sivel](https://forum.ansible.com/t/variables-not-consumed-from-delegated-host/40621/2) :

> delegation really only impacts connection related variables.
> 
> The task definition itself is templated under the actual host, but connection related vars are pulled from the delegated host.

Do I interpret that correctly, that ansible only uses `ansible_host` variable to _establish connection_ itself, but not for interpolating in templates of this task with `delegate_to`? In other words: “connection related variables” != template variables used for interpolation?

I cannot use `hostvars[somehost]`, as `somehost` is dynamic - `localhost` and `myhost` in this example - , task has conditional actions depending on the delegated host, like

```auto
temp_copy_directory: >-
  {{
    lookup('env','HOME')
    if ansible_host == 'localhost'
    else ansible_user_dir
  }}/tmp__ansible

```

So: Is there any way to refer to current delegated ansible host _from within task template_ ?

Thank you very much.

ansible 2.19.4

---

<div class="post-metadata">

**Author:** ![bvitnik](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bvitnik/32/4132_2.png) [@bvitnik](https://forum.ansible.com/u/bvitnik)\
**Post date:** [January 9, 2026, 1:20pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/2 "2026-01-09T13:20:39Z")

</div>

The relationship between `ansible_host` and `delegate_to` and the observed behavior is by design.

For a solution, I see no problem. You said that delegated host can be dynamic - in your example either `localhost` or `myhost`. That means that you have a task like:

```auto
- name: Delegated task
  <some_module>:
    <some_module_params>
    ...
  delegate_to: "{{ var_holding_delegated_host }}"

```

If so, than you can use `var_holding_delegated_host` in other expressions like:

```auto
hostvars[var_holding_delegated_host]

```

In other words, even if delegated host is dynamic, you should at all times have an information of which host the tasks were delegated to. You can use that information to extract vars related to the delegated host.

---

<div class="post-metadata">

**Author:** ![marvic](https://avatars.discourse-cdn.com/v4/letter/m/45deac/32.png) [@marvic](https://forum.ansible.com/u/marvic)\
**Post date:** [January 9, 2026, 2:41pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/3 "2026-01-09T14:41:13Z")

</div>

Yes right, that is one good solution. ~~Actually in my case , the distinction between ansible controller and nodes is sufficient, so I should be able to use condition `ansible_connection == “local”` .~~

_Update:_ Ah damn, `ansible_connection` oc has the same issue. And infos from previous `ansible.builtin.setup` with `delegate_facts: true` cannot be used either to get `ansible_user_dir` on delegated host. It seems passing that dynamic hostvar really is the only way.

* * *

What I also take away from your post: `delegate_to: D` uses connection-related variables like `ansible_host` etc. from `D` to only establish a connection to `D`, but then completely runs task template in variable context of original host / `inventory_hostname` - no substitution or adding of variables from delegated host context.

Thanks for the quick response!

---

<div class="post-metadata">

**Author:** ![marvic](https://avatars.discourse-cdn.com/v4/letter/m/45deac/32.png) [@marvic](https://forum.ansible.com/u/marvic)\
**Post date:** [January 9, 2026, 4:40pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/4 "2026-01-09T16:40:58Z")

</div>

Actually the situation with connection variables like `ansible_host`, `ansible_connection` etc. in combination with `delegate_to: D` is a bit more nuanced. Let’s take `ansible_host` as example:

- `ansible_host` from `D` is always used to establish a connection.
- For templating, if `inventory_hostname` has `ansible_host` explicitely declared (e.g. via host\_vars), then `ansible_host` is used from here.
- Otherwise `ansible_host` is taken from `D`.
- Other variable types are not taken at all from `D`.

That leads to the curiosity, that when resolving `ansible_host` via DNS, my playbook works, but when declaring `ansible_host` explicitely in host\_vars, it fails.

As connection variables are already handled in a specialized way for `delegate_to` directive and are always taken from delegated host context for connection establishment, why not adhere to same principles the other way round and always return delegate host values for `ansible_host` & Co. during template interpolation?

Is this purely based on historical decisions? I might be missing something - please correct me otherwise, but from bare user perspective current design chocie seems to be the most inconsistent and least intuitive.

See also [Variables not consumed from delegated host - #3 by kristianheljas](https://forum.ansible.com/t/variables-not-consumed-from-delegated-host/40621/3) .

---

<div class="post-metadata">

**Author:** ![bvitnik](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bvitnik/32/4132_2.png) [@bvitnik](https://forum.ansible.com/u/bvitnik)\
**Post date:** [January 9, 2026, 5:06pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/5 "2026-01-09T17:06:38Z")

</div>

While I can’t speak for the history or design decisions, I have a sense that you are using `delegate_to` the way it is not intended to be used. Host targeting should be done via play (`hosts:` key), not via `delegate_to`. `delegate_to` is mostly used for modules that must run on an Ansible controller host and communicate with some remote API or if you want to write some local files on your controller host. It can also be used in combination with `run_once` to target a specific host in some clustered setup (a master) so that your code does not run on other hosts in the cluster (slaves).

You have not described what you are really trying to do so I can’t tell if there is some alternative approach.

UPDATE: Don’t expect for this behavior to be changed as it would break other people’s code. It can only be documented better not to confuse people.

---

<div class="post-metadata">

**Author:** ![marvic](https://avatars.discourse-cdn.com/v4/letter/m/45deac/32.png) [@marvic](https://forum.ansible.com/u/marvic)\
**Post date:** [January 9, 2026, 6:04pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/6 "2026-01-09T18:04:08Z")

</div>

> [@bvitnik](#):
>
> I have a sense that you are using `delegate_to` the way it is not intended to be used. Host targeting should be done via play (`hosts:` key), not via `delegate_to`.

Yes, I am aware of the `hosts` directive and its relation to `delegate_to`. In principle my use case fits in the category described by [Controlling where tasks run: delegation and local actions](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_delegation.html):

> By default, Ansible gathers facts and executes all tasks on the machines that match the `hosts` line of your playbook. This page shows you how to delegate tasks to a different machine or group, delegate facts to specific machines or groups, or run an entire playbook locally.

There is a build host `B` with compile and development dependencies, and an execution host `E` with runtime dependencies. `B` compiles artifacts, which are then run by `E`. Each host has its own separate playbook assigned. Playbook for `B` and `E` can be started independently on their own. Playbook for `E` needs to copy artifacts over from `B`. Though of course `B` has to be run at least once in order to deliver artifacts to `E`.

Thus playbook for `E` is executed via `hosts: E`, but needs to use `delegate_to` in order to trigger copy process from `B`. `B` does not have SSH permissions to access `E`, hence copy process flow goes like B → Controller → E.

> [@bvitnik](#):
>
> UPDATE: Don’t expect for this behavior to be changed as it would break other people’s code. It can only be documented better not to confuse people.

That is understandable. But current behavior also is quite inconsistent. I better not count the time I needed to investigate into this issue.

What I mean by inconsistent: You use `inventory_hostname`, when referring to original host in a task with `delegate_to`. In this task, if intention is to refer to delegated host, you would expect, the host variable already used for the ssh connection `ansible_host` points to the same host during template. Getting a different host back is confusing.

If I read correctly, in the past, there existed `ansible_delegated_host` variable or similar, which always pointed at the delegated host - does it still exist (if not, why)? IMO It would be a good idea to (re)introduce it for a more consistent way to reference the delegate host for the long run, while not breaking anything in short-term. In future, a breaking release might “fix” `ansible_host`, and `ansible_delegated_host` could just point back to it, but there is no hurry about this.

Does such a variable still exist?

---

<div class="post-metadata">

**Author:** ![bvitnik](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bvitnik/32/4132_2.png) [@bvitnik](https://forum.ansible.com/u/bvitnik)\
**Post date:** [January 9, 2026, 8:26pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/7 "2026-01-09T20:26:48Z")

</div>

While I cannot answer all your questions (I’m leaving that to Ansible core devs), I can tell you how to alleviate your problem given your example of `B`, `Controller` (let’s call it `C`) and `E` hosts.

You have two playbooks, let’s call them `B.yml` and `E.yml`, that are meant to be run against host `B` and `E` respectively.

```yaml
# B.yml
---
- name: Build artifacts on B
  hosts: B

  tasks:
  - name: Build
    <some_module>:
      <some_module_params>
      ...
    # no delegate_to

```

```yaml
# E.yml
---
- name: Fetch artifacts from B to C
  hosts: localhost
  gather_facts: false
  
  tasks:
  - name: Fetch artifacts
    shell:
      <shell_params>
      ...
    # shell params specify B host as source
    # no delegate_to

- name: Run artifacts on E
  hosts: E

  tasks:
  - name: Copy artifacts from C to E
    copy:
      <copy_module_params>
      ...
    # no delegate_to    

  - name: Run artifacts
    <some_module>:
      <some_module_params>
      ...
    # no delegate_to

```

So the idea is to move the artifact fetching stage to run in a separate play (still `E.yml` playbook) that runs on `C`. This removes the need to use `delegate_to` at all. You can play with the idea.

UPDATE: I assume that fetching stage involves some additional steps that must run on `C` before `E` can run. If only file copying is involved, `E.yml` can also be implemented this way:

```yaml
# E.yml
---
- name: Fetch artifacts from B to C
  hosts: B # <- note this change
  gather_facts: false
  
  tasks:
  - name: Fetch artifacts
    fetch: # <- and this
      <fetch_params>
      ...
    # no delegate_to

- name: Run artifacts on E
  hosts: E

  tasks:
  - name: Copy artifacts from C to E
    copy:
      <copy_module_params>
      ...
    # no delegate_to    

  - name: Run artifacts
    <some_module>:
      <some_module_params>
      ...
    # no delegate_to

```

---

<div class="post-metadata">

**Author:** ![marvic](https://avatars.discourse-cdn.com/v4/letter/m/45deac/32.png) [@marvic](https://forum.ansible.com/u/marvic)\
**Post date:** [January 9, 2026, 9:27pm UTC](https://forum.ansible.com/t/ansible-host-with-delegate-to-points-to-inventory-hostname-not-delegated-host/45075/8 "2026-01-09T21:27:26Z")

</div>

Thank you so much for your time @bvitnik !

You are right, dividing tasks amongst different plays in proper order with specific `hosts:` makes things easier. This way, I probably can get rid of `delegate_to`.

If there should be need to reference delegated host from task template, it is probably best practice to just use explicit variables and avoid `ansible_host` & Co. in tasks that use `delegate_to`.

I still would be curious about devs’ opinion about fixing connection variables to always point to delegated host for the sake of consistency and not being overly confusing. Currently lacking a use case though 🙂 . I’ll tinker with your solution, and see how it goes.
