From 5e662fedc6334c9f8619c31dd01eb4872fdd3be1 Mon Sep 17 00:00:00 2001 From: hanli <37435717+hnl1@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:01:59 +0800 Subject: [PATCH 1/5] feat: add --unix-socket option for Unix socket listening Add optional --unix-socket CLI flag that uses aiohttp.web.UnixSite instead of TCPSite. When specified, --listen and --port are ignored. TCP remains the default behavior when the flag is not provided. --- comfy/cli_args.py | 1 + main.py | 18 ++++++++++-------- server.py | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/comfy/cli_args.py b/comfy/cli_args.py index 63daca861..2c3906c56 100644 --- a/comfy/cli_args.py +++ b/comfy/cli_args.py @@ -37,6 +37,7 @@ parser = argparse.ArgumentParser() parser.add_argument("--listen", type=str, default="127.0.0.1", metavar="IP", nargs="?", const="0.0.0.0,::", help="Specify the IP address to listen on (default: 127.0.0.1). You can give a list of ip addresses by separating them with a comma like: 127.2.2.2,127.3.3.3 If --listen is provided without an argument, it defaults to 0.0.0.0,:: (listens on all ipv4 and ipv6)") parser.add_argument("--port", type=int, default=8188, help="Set the listen port.") +parser.add_argument("--unix-socket", type=str, default=None, help="Path to Unix socket file to listen on. When specified, --listen and --port are ignored.") parser.add_argument("--tls-keyfile", type=str, help="Path to TLS (SSL) key file. Enables TLS, makes app accessible at https://... requires --tls-certfile to function") parser.add_argument("--tls-certfile", type=str, help="Path to TLS (SSL) certificate file. Enables TLS, makes app accessible at https://... requires --tls-keyfile to function") parser.add_argument("--enable-cors-header", type=str, default=None, metavar="ORIGIN", nargs="?", const="*", help="Enable CORS (Cross-Origin Resource Sharing) with optional origin or allow all with default '*'.") diff --git a/main.py b/main.py index 3fe8f0589..53d9ed05a 100644 --- a/main.py +++ b/main.py @@ -304,13 +304,15 @@ def prompt_worker(q, server_instance): hook_breaker_ac10a0.restore_functions() -async def run(server_instance, address='', port=8188, verbose=True, call_on_start=None): - addresses = [] - for addr in address.split(","): - addresses.append((addr, port)) - await asyncio.gather( - server_instance.start_multi_address(addresses, call_on_start, verbose), server_instance.publish_loop() - ) +async def run(server_instance, address='', port=8188, unix_socket=None, verbose=True, call_on_start=None): + if unix_socket: + start_coro = server_instance.start_unix_socket(unix_socket, verbose) + else: + addresses = [] + for addr in address.split(","): + addresses.append((addr, port)) + start_coro = server_instance.start_multi_address(addresses, call_on_start, verbose) + await asyncio.gather(start_coro, server_instance.publish_loop()) def hijack_progress(server_instance): def hook(value, total, preview_image, prompt_id=None, node_id=None): @@ -419,7 +421,7 @@ def start_comfyui(asyncio_loop=None): async def start_all(): await prompt_server.setup() - await run(prompt_server, address=args.listen, port=args.port, verbose=not args.dont_print_server, call_on_start=call_on_start) + await run(prompt_server, address=args.listen, port=args.port, unix_socket=args.unix_socket, verbose=not args.dont_print_server, call_on_start=call_on_start) # Returning these so that other code can integrate with the ComfyUI loop and server return asyncio_loop, prompt_server, start_all diff --git a/server.py b/server.py index 275bce5a7..23f4a91a2 100644 --- a/server.py +++ b/server.py @@ -1219,6 +1219,27 @@ class PromptServer(): if call_on_start is not None: call_on_start(scheme, self.address, self.port) + async def start_unix_socket(self, unix_socket, verbose=True): + if sys.platform == 'win32': + raise RuntimeError("Unix sockets are not supported on Windows. Please use --listen and --port instead.") + + runner = web.AppRunner(self.app, access_log=None) + await runner.setup() + + if verbose: + logging.info("Starting server\n") + + if os.path.exists(unix_socket): + os.unlink(unix_socket) + site = web.UnixSite(runner, unix_socket) + await site.start() + os.chmod(unix_socket, 0o666) + self.address = unix_socket + self.port = None + self.unix_socket = unix_socket + if verbose: + logging.info("Listening on Unix socket: {}".format(unix_socket)) + def add_on_prompt_handler(self, handler): self.on_prompt_handlers.append(handler) From 3ef720c87e7b468821fe6cf6c21d75850f8f5d49 Mon Sep 17 00:00:00 2001 From: hanli <37435717+hnl1@users.noreply.github.com> Date: Sat, 28 Feb 2026 13:47:56 +0800 Subject: [PATCH 2/5] fix: validate socket file type before removal and tighten permissions - Check file type with stat.S_ISSOCK before unlinking to prevent accidental deletion of non-socket files - Change socket permissions from 0o666 to 0o660 for better security --- server.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index 23f4a91a2..94397aa24 100644 --- a/server.py +++ b/server.py @@ -1,5 +1,6 @@ import os import sys +import stat import asyncio import traceback import time @@ -1229,11 +1230,14 @@ class PromptServer(): if verbose: logging.info("Starting server\n") - if os.path.exists(unix_socket): + if os.path.lexists(unix_socket): + st_mode = os.lstat(unix_socket).st_mode + if not stat.S_ISSOCK(st_mode): + raise RuntimeError(f"Refusing to remove non-socket path: {unix_socket}") os.unlink(unix_socket) site = web.UnixSite(runner, unix_socket) await site.start() - os.chmod(unix_socket, 0o666) + os.chmod(unix_socket, 0o660) self.address = unix_socket self.port = None self.unix_socket = unix_socket From 4b616bca4f513edc033baa84811468d1e995ac59 Mon Sep 17 00:00:00 2001 From: hanli <37435717+hnl1@users.noreply.github.com> Date: Sat, 28 Feb 2026 13:52:15 +0800 Subject: [PATCH 3/5] docs: add docstring to start_unix_socket method --- server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server.py b/server.py index 94397aa24..a59a407ec 100644 --- a/server.py +++ b/server.py @@ -1221,6 +1221,7 @@ class PromptServer(): call_on_start(scheme, self.address, self.port) async def start_unix_socket(self, unix_socket, verbose=True): + """Start the server listening on a Unix domain socket instead of TCP.""" if sys.platform == 'win32': raise RuntimeError("Unix sockets are not supported on Windows. Please use --listen and --port instead.") From 397bd47bc22e7007a7c831a26fcd45f8ebc0d3b3 Mon Sep 17 00:00:00 2001 From: hanli <37435717+hnl1@users.noreply.github.com> Date: Sat, 28 Feb 2026 13:58:06 +0800 Subject: [PATCH 4/5] fix: stop site on chmod failure to avoid running with wrong permissions --- server.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server.py b/server.py index a59a407ec..62ff10c07 100644 --- a/server.py +++ b/server.py @@ -1238,7 +1238,12 @@ class PromptServer(): os.unlink(unix_socket) site = web.UnixSite(runner, unix_socket) await site.start() - os.chmod(unix_socket, 0o660) + try: + os.chmod(unix_socket, 0o660) + except OSError as e: + await site.stop() + await runner.cleanup() + raise RuntimeError(f"Failed to set socket permissions: {e}") self.address = unix_socket self.port = None self.unix_socket = unix_socket From 807e9fbe884cc51a19fed42999b07b21f3173121 Mon Sep 17 00:00:00 2001 From: hanli <37435717+hnl1@users.noreply.github.com> Date: Sat, 28 Feb 2026 14:03:46 +0800 Subject: [PATCH 5/5] fix: ensure runner cleanup on any unix socket startup failure --- server.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/server.py b/server.py index 62ff10c07..7b31e6992 100644 --- a/server.py +++ b/server.py @@ -1225,9 +1225,6 @@ class PromptServer(): if sys.platform == 'win32': raise RuntimeError("Unix sockets are not supported on Windows. Please use --listen and --port instead.") - runner = web.AppRunner(self.app, access_log=None) - await runner.setup() - if verbose: logging.info("Starting server\n") @@ -1236,14 +1233,19 @@ class PromptServer(): if not stat.S_ISSOCK(st_mode): raise RuntimeError(f"Refusing to remove non-socket path: {unix_socket}") os.unlink(unix_socket) - site = web.UnixSite(runner, unix_socket) - await site.start() + + runner = web.AppRunner(self.app, access_log=None) + await runner.setup() try: + site = web.UnixSite(runner, unix_socket) + await site.start() os.chmod(unix_socket, 0o660) except OSError as e: - await site.stop() await runner.cleanup() - raise RuntimeError(f"Failed to set socket permissions: {e}") + raise RuntimeError(f"Failed to set socket permissions: {e}") from e + except Exception: + await runner.cleanup() + raise self.address = unix_socket self.port = None self.unix_socket = unix_socket