Skip to main content

Proxmox VE Host Name Changing On Existing Server

When setting up a Proxmox VE environment it is common to end up with a node named pve, which is the default hostname assigned during installation. This becomes a problem when managing multiple Proxmox servers since every node will show up as pve in the web UI. This post covers the complete process for renaming a Proxmox node to a meaningful name including the FQDN, and how to handle the pmxcfs filesystem quirks that make this more involved than a standard Linux hostname change.

Understanding How Proxmox Uses the Hostname

Proxmox VE derives the node name directly from the short hostname (the part before the first dot). This means a server configured with the FQDN pve-node-1.example.lan will appear in the UI as pve-node-1. It is important to plan the naming convention carefully upfront. A naming scheme like pve-node-1, pve-node-2 works well because the short hostname is already fully unique, without relying on the domain portion to differentiate nodes.

Proxmox stores its configuration in a virtual FUSE filesystem called pmxcfs, which is backed by a SQLite database at /var/lib/pve-cluster/config.db. This filesystem has restrictions that prevent renaming non-empty folders, which is why a Proxmox node rename requires more than just editing /etc/hostname.

Step 1: Update /etc/hostname

Replace the contents of /etc/hostname with the new short hostname:

echo "pve-node-1" > /etc/hostname

Step 2: Update /etc/hosts

The /etc/hosts file must map the new FQDN and short hostname to the server's static IP address. The pmxcfs service will fail to start if it cannot resolve the node name to a non-loopback IP, so this step is critical.

127.0.0.1       localhost
192.168.1.x     pve-node-1.example.lan    pve-node-1

Step 3: Update Postfix

Edit /etc/postfix/main.cf and update the myhostname value to the new FQDN:

myhostname = pve-node-1.example.lan

Step 4: Rename the Node in the pmxcfs Database

This is where Proxmox node renames differ from a standard Linux hostname change. The node name is stored in the SQLite database that backs pmxcfs. The official Proxmox documentation notes that pmxcfs cannot rename non-empty folders, so standard file copy approaches will not work reliably. The cleanest solution is to rename the node directly in the database while the pve-cluster service is stopped.

One important note: pmxcfs reads the node name from /etc/hostname at startup and requires it to be resolvable in /etc/hosts. If the hostname has already been changed but the database still references the old name, pmxcfs will fail to start with the error:

Unable to resolve node name 'pve' to a non-loopback IP address

If this situation occurs, temporarily add the old hostname as an alias in /etc/hosts to get pve-cluster running, then proceed with the database update.

Stop the pve-cluster service:

systemctl stop pve-cluster

Rename the node in the database. The nodes directory in the tree table uses inode as the primary key. First confirm the schema and identify the correct row:

sqlite3 /var/lib/pve-cluster/config.db "SELECT inode, parent, name FROM tree WHERE name IN ('pve', 'nodes');"

Then update the node name directly:

sqlite3 /var/lib/pve-cluster/config.db "UPDATE tree SET name='pve-node-1' WHERE name='pve' AND parent=(SELECT inode FROM tree WHERE name='nodes');"

Verify the change looks correct:

sqlite3 /var/lib/pve-cluster/config.db "SELECT inode, parent, name FROM tree WHERE name IN ('pve', 'pve-node-1', 'nodes');"

The output should show only the new name with no remaining pve entry under nodes. Start pve-cluster back up:

systemctl start pve-cluster

Step 5: Copy RRD Statistics Data

Proxmox stores historical performance data for nodes and storage in RRD files under /var/lib/rrdcached/db/. These are keyed by node name, so they need to be copied to match the new name. If the old directories do not exist this step can be skipped.

cp -r /var/lib/rrdcached/db/pve2-node/pve /var/lib/rrdcached/db/pve2-node/pve-node-1
cp -r /var/lib/rrdcached/db/pve2-storage/pve /var/lib/rrdcached/db/pve2-storage/pve-node-1
rm -r /var/lib/rrdcached/db/pve2-node/pve
rm -r /var/lib/rrdcached/db/pve2-storage/pve

Step 6: Reboot and Verify

reboot

After the server comes back up, confirm the rename was successful:

hostname        # should return: pve-node-1
hostname -f     # should return: pve-node-1.example.lan
ls /etc/pve/nodes/    # should show only pve-node-1

The web UI should reflect the new node name with all VMs and containers listed correctly under the renamed node.

Things to Watch Out For

1) SSL certificates — The self-signed certificate generated by Proxmox is tied to the hostname. It will regenerate automatically on reboot, so expect a new browser warning to accept. If Let's Encrypt is configured through the Proxmox UI it will need to be re-issued after the rename.

2) pmxcfs caches its state in memory — Any direct edits to the SQLite database must be made while pve-cluster is stopped. If pve-cluster is running when edits are made, it will overwrite the database with its in-memory state on the next restart, undoing the changes.

3) The node name is always the short hostname — pmxcfs derives the node name from the hostname, not the FQDN. A server configured with FQDN pve-node-1.example.lan will show as pve-node-1 in the UI. Do not rely on the domain portion to differentiate nodes — ensure the short hostname itself is unique across all servers.

4) Do not attempt this on a clustered node — Renaming a node that is a member of a Proxmox cluster requires additional steps to update the corosync configuration and is outside the scope of this post.

Comments

Popular posts from this blog

Validating User Input In CRM Portals With JavaScript

When we are setting up CRM Portals to allow customers to update their information, open cases, fill out an applications, etc. We want to make sure that we are validating their input before it is committed to CRM.  This way we ensure that our data is clean and meaningful to us and the customer. CRM Portals already has a lot validation checks built into it. But, on occasion we need to add our own.  To do this we will use JavaScript to run the validation and also to output a message to the user to tell them there is an issue they need to fix. Before we can do any JavaScript, we need to check and see if we are using JavaScript on an Entity Form or Web Page.  This is because the JavaScript, while similar, will be different.  First, we will go over the JavaScript for Entity Forms.  Then, we will go over the JavaScript for Web Pages.  Finally, we will look at the notification JavaScript. Entity Form: if (window.jQuery) { (function ($) { if ...

Power Pages Update Last Successful Login Using JavaScript and Power Pages API

 Recently while working on a Power Pages implementation for a client, I had the requirement to show the last time a user logged in on their profile page.  I thought this would be easy to do as there is already a field on the contact record for "Last Successful Login" (      adx_identity_lastsuccessfullogin).  This use to update when a user logged in, but it appears Microsoft has removed that automation. While searching I came across a few different ways of achieving this task.  One used application insights in Azure and another one used an HTTP endpoint setup in Power Automate.  I thought, this needs to be simpler.  What I came up with is to use Liquid with JavaScript to tell if a user is logged in or not.  Then use the new Power Pages api to update the logged in users contact record to mark the last time they logged in. Here is the approach I setup: 1) Make sure you turn on the api for contact in Site Settings. 1) Link to Microsoft Do...

Reusable Method To Get Record By Id

I have a handful of reusable code that I use when creating plugins or external process (i.e. Azure Functions) for working with DataVerse. The first one I am providing is Getting a Record By Id: 1: private static Entity GetFullRecord(string entityName, string primaryKey, Guid recordId, IOrganizationService service) 2: { 3: using (OrganizationServiceContext context = new OrganizationServiceContext(service)) 4: { 5: return (from e in context.CreateQuery(entityName) 6: where (Guid)e[primaryKey] == recordId 7: select e).Single(); 8: } 9: } entityName = The logical name of the entity primaryKey = The primary key field for the entity. If using late binding you can create this dynamically by doing: $"{target.LogicalName}id" recordId = Guid of the record to get service = Service to interact with DataVerse