# How to migrate a self-hosted controller and router to a new VM and domain?

**URL:** <https://openziti.discourse.group/t/how-to-migrate-a-self-hosted-controller-and-router-to-a-new-vm-and-domain/5820>\
**Category:** Ziti Overlay\
**Created:** [May 7, 2026, 2:32pm UTC](https://openziti.discourse.group/t/how-to-migrate-a-self-hosted-controller-and-router-to-a-new-vm-and-domain/5820 "2026-05-07T14:32:23Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![lex529](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/lex529/32/2940_2.png) [@lex529](https://openziti.discourse.group/u/lex529)\
**Post date:** [May 7, 2026, 2:32pm UTC](https://openziti.discourse.group/t/how-to-migrate-a-self-hosted-controller-and-router-to-a-new-vm-and-domain/5820/1 "2026-05-07T14:32:23Z")

</div>

Hi all, I'm running a self-hosted OpenZiti deployment on a single Azure VM, where the controller and the edge router live together, it was setup using the install script, i'm planning to switch that do docker.

The VM currently uses the azure provided domain, so this will change. I want to move the controller + router onto a new VM with a new hostname/domain. The new VM will be hosted on a different infra provider. The domain change is the part that concerns me the most.

The network isn't huge, but i would like to avoid reenlisting everything. Here's a summary of what's currently enrolled/running:

- Controller: 1
- Identities: 13
- Edge routers: 2 (one in the vm, one inside a k8s cluster)
- Services: 12

What's the cleanest migration path?

I've setup the entire thing using terraform, so recreating everything will not be such a big issue, reenrolling everything is gonna be a bit cumbersome as i don't have direct access to all the devices, i would like to avoid this if i can without doing a lot of work.

The closest thing that i could find for my problem is

> [@Change Controller Adresse/Port or migrate to a new controller](https://openziti.discourse.group/t/change-controller-adresse-port-or-migrate-to-a-new-controller/3495):
>
> So I have a medium sized setup: 1 Controller, 2 Public edge routers and about 12 private routers and 20 clients. Currently the controller is reachable via [ctrl.example.com:1280](http://ctrl.example.com:1280) and the edge routers via [edge1.example.com:3022](http://edge1.example.com:3022) and [edge2.example.com:3022](http://edge2.example.com:3022). My goal is to change the address or especially the port of the controller to be reachable via port 443 and the edge1 via [edge1.example.com:80](http://edge1.example.com:80). Is there anyway to achieve this without re-enrolling every router and client? I know that the config…

But here it's only a port change, and also the post is quite old 1.5 years ago, i saw that there is a clustering mode for controllers now and i was hopping that it might help.

Any pointers to docs, existing migration guides, or community experience with controller relocation would be very much appreciated. Thanks!

---

<div class="post-metadata">

**Author:** ![tekook](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/tekook/32/2801_2.png) [@tekook](https://openziti.discourse.group/u/tekook)\
**Post date:** [May 7, 2026, 2:51pm UTC](https://openziti.discourse.group/t/how-to-migrate-a-self-hosted-controller-and-router-to-a-new-vm-and-domain/5820/2 "2026-05-07T14:51:01Z")

</div>

I think u can use the exact same approach.

The certificates don’t contain the domain name, so you should not have a changing the domain or port the same way I did.

You just need to make sure to first point the domain to the currenr system before migrating the controller to a new VM.

Could also do the other way around.

I don’t think you can the controller on both VM simultaneously.

---

<div class="post-metadata">

**Author:** ![lex529](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/lex529/32/2940_2.png) [@lex529](https://openziti.discourse.group/u/lex529)\
**Post date:** [May 11, 2026, 9:19am UTC](https://openziti.discourse.group/t/how-to-migrate-a-self-hosted-controller-and-router-to-a-new-vm-and-domain/5820/3 "2026-05-11T09:19:56Z")

</div>

> [@tekook](#):
>
> You just need to make sure to first **point the domain** to the currenr system before migrating the controller to a new VM.

but i will completely change the domain, the current domain is `....cloudapp.azure.com`, this will definitely change.  
Or do you mean after i setup the new vm + domain, have the new domain point to the azure vm?

---

<div class="post-metadata">

**Author:** ![TheLumberjack](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/thelumberjack/32/113_2.png) [@TheLumberjack](https://openziti.discourse.group/u/TheLumberjack)\
**Post date:** [May 11, 2026, 1:48pm UTC](https://openziti.discourse.group/t/how-to-migrate-a-self-hosted-controller-and-router-to-a-new-vm-and-domain/5820/4 "2026-05-11T13:48:59Z")

</div>

Hi @lex529,

> but i will completely change the domain, the current domain is ....cloudapp.azure.com, this will definitely change

That's the exact case `newListener` (ctrl channel) and `newAddress` (REST/edge API) were built for -- a domain change without re-enrolling. They were added in  
[v0.24.5](https://github.com/openziti/ziti/releases/tag/v0.24.5). Routers learn the new ctrl address over the control channel, and SDK clients pick up a `Ziti-Ctrl-Address` HTTP response  
header from the old controller and auto-rewrite their identity `ztAPI` on the next request -- no re-enrollment, no per-client edits.

The rough flow I'd try:

1. Regenerate the controller's ctrl and edge server certs to include BOTH the old and new hostnames as SANs (the controller refuses these settings if the new hostname isn't in the cert).
2. Add `newListener: tls:NEW_DOMAIN:8440` under `ctrl.options` and `newAddress: NEW_DOMAIN:8441` under each `web.bindPoints[]` on the old controller, then restart it -- routers reconnect  
and record the new ctrl address; clients receive the header and rewrite their config.
3. Stop the old controller, copy the quickstart dir (configs + pki + db) to the new VM at the same absolute path, flip `advertiseAddress` and each bindPoint `address` to `NEW_DOMAIN`, drop  
the `newListener` / `newAddress` lines, and start there.
4. Bring the old controller back up briefly while clients drain -- mind that the two controllers' bbolt DBs diverge from the moment the new one starts, so keep the window short and only  
make admin changes against the new one. Once everyone has drained, stop the old one for good.

On the Docker side: I have tested the above on plain VMs running the controller under systemd, not in a container, so you'll have to work out the Docker-specific bits yourself -- mounting the  
migrated quickstart dir into the container (same absolute paths inside the container as the yaml expects), publishing 8440/8441, pointing the container's command at the same yaml, and  
getting your TLS cert paths visible inside the container. The migration logic itself doesn't change, only the packaging.
