diff --git a/common/src/foundation/python-packages/pyproject.toml b/common/src/foundation/python-packages/pyproject.toml
index cfd8d45b6..9c5f3bc72 100644
--- a/common/src/foundation/python-packages/pyproject.toml
+++ b/common/src/foundation/python-packages/pyproject.toml
@@ -108,5 +108,5 @@ cryptography = { appliances = ["server"] }
 jinja2 = { appliances = ["server"], bootstrap = true }
 markupsafe = { appliances = ["server"] }
 pycparser = { appliances = ["server"] }
-pyyaml = { appliances = ["server"], bootstrap = true }
+pyyaml = { appliances = ["server", "client"], bootstrap = true }
 six = { appliances = ["server"] }
diff --git a/common/src/stack/command/stack/commands/sync/host/config/plugin_smq.py b/common/src/stack/command/stack/commands/sync/host/config/plugin_smq.py
new file mode 100644
index 000000000..ffde40358
--- /dev/null
+++ b/common/src/stack/command/stack/commands/sync/host/config/plugin_smq.py
@@ -0,0 +1,30 @@
+import stack.commands
+from pathlib import Path
+from stack.util import _exec
+
+class Plugin(stack.commands.Plugin):
+	"""
+	Sync smq settings to hosts and restart all smq related services
+	"""
+
+	def provides(self):
+		return 'smq'
+
+	def requires(self):
+		return []
+
+	def run(self, args):
+		hosts = args['hosts']
+		settings_file = Path('/opt/stack/etc/stacki.yml')
+		services = ['smq-processor', 'smq-producer', 'smq-publisher', 'smq-shipper']
+
+		for host in hosts:
+			self.owner.notify('Sync SMQ Settings')
+			if settings_file.is_file():
+				copy_settings = _exec(f'scp {settings_file} {host}:{settings_file}', shlexsplit=True)
+				if copy_settings.returncode != 0:
+					self.owner.notify(f'Failed to copy settings file to {host}:{copy_settings.stderr}')
+			for service in services:
+				restart_smq = _exec(f'ssh -t {host} "systemctl restart {service}" ', shlexsplit=True)
+				if restart_smq.returncode != 0:
+					self.owner.notify(f'Failed to restart service {service} on host {host}')
diff --git a/common/src/stack/mq/pylib/mq/__init__.py b/common/src/stack/mq/pylib/mq/__init__.py
index 882075769..3b84448c9 100644
--- a/common/src/stack/mq/pylib/mq/__init__.py
+++ b/common/src/stack/mq/pylib/mq/__init__.py
@@ -10,6 +10,8 @@
 import socket
 import sys
 import os
+import yaml
+from pathlib import Path
 
 # We need the "ports" class when we are inside the installer, but don't need to
 # setup and zmq based services. The alternative is to add zmq and dependencies
@@ -23,18 +25,49 @@
 
 
 
-class ports:
+class PortsMeta(type):
 	"""
 	Socket port numbers used by the Stack Message Queue daemons.
 
-	:var publish: UDP socket service for publishing a message
-	:var subscribe: zmq.SUB socket for subscribing to a channel
-	:var control: TCP socket service for enabling/disabling channel propagation
+	:publish: UDP socket service for publishing a message
+	:subscribe: zmq.SUB socket for subscribing to a channel
+	:control: TCP socket service for enabling/disabling channel propagation
 	"""
-	publish	  = 5000
-	subscribe = 5001
-	control	  = 5002
 
+	# Stacki settings file location
+	# and default ports
+	SETTINGS = Path('/opt/stack/etc/stacki.yml')
+	pub_port = 5000
+	sub_port = 5001
+	con_port = 5002
+
+	def load_settings(cls):
+		config = {}
+		if cls.SETTINGS.is_file():
+			with cls.SETTINGS.open() as f:
+				config = yaml.safe_load(f)
+		return config
+
+	@property
+	def publish(cls):
+		config = cls.load_settings()
+		port = int(config.get('smq.pub.port', cls.pub_port))
+		return port
+
+	@property
+	def subscribe(cls):
+		config = cls.load_settings()
+		port = int(config.get('smq.sub.port', cls.sub_port))
+		return port
+
+	@property
+	def control(cls):
+		config = cls.load_settings()
+		port = int(config.get('smq.control.port', cls.con_port))
+		return port
+
+class ports(metaclass=PortsMeta):
+	pass
 
 class Message():
 	"""
@@ -42,7 +75,7 @@ class Message():
 
 	A Message is composed of header fields and the *message* text body.  For many
 	applications only body is manipulated and other fields are controlled by
-	lower software levels.  
+	lower software levels.
 
 	For simple Messages the *message* body can be a string.  For more complex Messages
 	the body should be a json encoded python dictionary.
@@ -87,7 +120,7 @@ def __init__(self, payload=None, *, message=None, channel=None, hops=None, ttl=N
 
 		# JSON does not override parameters, this allows loading an
 		# existing Message and overwriting some of the fields
-		
+
 		self.channel = channel if channel else msg.get('channel')
 		self.id      = id      if id      else msg.get('id')
 		self.payload = payload if payload else msg.get('payload')
@@ -160,7 +193,7 @@ def getPayload(self):
 	def setPayload(self, payload):
 		"""
 		Sets the payload text
-		
+
 		:param payload: text
 		:type payload: string
 		"""
@@ -169,7 +202,7 @@ def setPayload(self, payload):
 
 	def getHops(self):
 		"""
-		:returns: number of software hops 
+		:returns: number of software hops
 		"""
 		return self.hops
 
@@ -189,7 +222,7 @@ def setSource(self, addr):
 		"""
 		Set the source host address.  This address can be a hostname
 		or an IP Address.
-		
+
 		:param addr: source address
 		:type addr: string
 		"""
@@ -234,9 +267,9 @@ def setID(self, id):
 	def addHop(self):
 		"""
 		Increments the hop count for the :class:`Message`.  A hop is
-		defined as a software hop not a physical network hop.  
+		defined as a software hop not a physical network hop.
 		Every time an application receives and retransmits a message the
-		hop should be incremented.  
+		hop should be incremented.
 		This value is used to debugging.
 		"""
 		self.hops += 1
@@ -289,12 +322,12 @@ def unsubscribe(self, channel):
 		:type channel: string
 		"""
 		self.sub.setsockopt_string(zmq.UNSUBSCRIBE, channel)
-		
+
 	def run(self):
 		while True:
 			try:
 				channel, payload = self.sub.recv_multipart()
-				msg = Message(message=payload.decode(), 
+				msg = Message(message=payload.decode(),
 					      channel=channel.decode())
 			except:
 				continue
@@ -341,10 +374,10 @@ def run(self):
 			#
 			# Note the callback() always pushes data as
 			# stack.mq.Message objects.  This is the only
-			# part of the code where we handle receiving 
+			# part of the code where we handle receiving
 			# unstructured data.
 			#
-			# Design point here was to keep the clients 
+			# Design point here was to keep the clients
 			# simple so we don't need an API to write to
 			# the message queue.
 
diff --git a/common/src/stack/pylib/stack/settings.py b/common/src/stack/pylib/stack/settings.py
index f16e30060..41292217e 100644
--- a/common/src/stack/pylib/stack/settings.py
+++ b/common/src/stack/pylib/stack/settings.py
@@ -35,6 +35,11 @@
 # these settings control the starting points for discover-nodes (nee insert ethers)
 discovery.base.rack: '0'
 discovery.base.rank: '0'
+
+# smq ports
+smq.pub.port: '5000'
+smq.sub.port: '5001'
+smq.control.port: '5002'
 '''
 
 def get_settings():
diff --git a/redhat/nodes/backend.xml b/redhat/nodes/backend.xml
index 4d26dd6d4..c34a23dcb 100644
--- a/redhat/nodes/backend.xml
+++ b/redhat/nodes/backend.xml
@@ -37,7 +37,7 @@ for o in stack.api.Call('list cart'):
 
 
 
- 
+
 
 
 /opt/stack/bin/stacki-status.py Running preinstall
@@ -50,11 +50,21 @@ include ld.so.conf.d/*.conf
 
 
 /opt/stack/bin/stacki-status.py install complete rebooting
+
+cat /opt/stack/etc/stacki.yml;
+
+
+
+
+
+import stack.settings
+
+stack.settings.write_default_settings_file(overwrite=True)
 
 
 
 cp /run/install/tmp/stack.conf /tmp/stack.conf
 
 
- 
+
 
diff --git a/sles/nodes/backend.xml b/sles/nodes/backend.xml
index e38ed3d05..dbb125cd0 100644
--- a/sles/nodes/backend.xml
+++ b/sles/nodes/backend.xml
@@ -1,5 +1,5 @@
 
-	
+
 	
 	Handle for Backend Appliance
 	
@@ -56,6 +56,11 @@ rm -f /etc/zypp/repos.d/*
 &hostname;
 
 
+
+
+cat /opt/stack/etc/stacki.yml;
+
+
 
 
 &hostname;
@@ -69,6 +74,9 @@ rm -f /etc/zypp/repos.d/*
 &hostname;
 &hostname;
 &hostname;
+
+cat /opt/stack/etc/stacki.yml;
+
 
 #
 # the above command (report host resolv) writes /etc/resolv.conf. make sure
@@ -102,5 +110,5 @@ rm -rf /tmp/ipmisetup
 
 
 
- 
+