High-Performance Communication Between Go and Python Using Unix Domain Sockets
In modern software systems, it is very common to combine multiple programming languages within the same project. Each language excels at different things: Go is excellent for building high-performance backend services, while Python often shines when working with device protocols, rapid prototyping, and mature third-party ecosystems.
However, once you decide to mix languages, a critical question appears:
How should these components communicate with each other?
During a recent real-time video management project I was working on, I faced exactly this challenge. The backend API layer was written in Go, while all camera-side operations—ONVIF communication, user management, RTSP discovery—were handled by a Python worker. Both services were running on the same machine, but they needed to exchange messages frequently and reliably.
Opening TCP ports, managing firewalls, and introducing unnecessary network layers felt like the wrong direction. What I needed was something faster, safer, and simpler.
That’s when Unix Domain Sockets became the obvious choice.
When the first prototype started working, the experience was striking. Go and Python felt like they were no longer communicating over a network at all—they were simply exchanging messages inside the kernel, fast and effortlessly. This article is a deep dive into that experience, the architecture behind it, and why Unix Domain Sockets can be a hidden superpower in local service communication.
What Is a Unix Domain Socket?
A Unix Domain Socket (UDS) is a mechanism that allows inter-process communication (IPC) between processes running on the same operating system instance, without using the network stack.
From a programming perspective, it behaves very similarly to TCP sockets. From a system perspective, however, it is fundamentally different.
A simple analogy:
If TCP communication is like making a phone call across cities, Unix Domain Sockets are like two people talking in the same room.
No routing, no ports, no IP addresses—just direct communication through the kernel.
The Technical Foundation of UDS
A Unix Domain Socket is represented as a special file in the filesystem:
/run/deviceiq/onvif.sock
/tmp/app.sock
/var/run/docker.sock
Important details:
- This file is not written to disk
- It exists as a kernel-managed endpoint
- The filesystem path is only an address
- Data is transferred through kernel memory buffers
Applications open, read, and write to this file as if it were a socket, but the kernel handles everything internally.
Why Is UDS Faster Than TCP?
Unix Domain Sockets avoid many expensive layers of TCP/IP communication.
1. No Network Stack
TCP data flow:
Application → Kernel → TCP/IP stack → Routing → Firewall → Peer
UDS data flow:
Application A → Kernel → Application B
2. No Handshake
TCP requires a three-way handshake (SYN, SYN-ACK, ACK).
UDS connections can start sending data immediately.
3. No Packet Fragmentation
Data is transferred as a stream inside the kernel, not as network packets.
4. Fewer Context Switches
Less overhead means lower CPU usage and more stable latency.
In practice, UDS is often 40–60% faster than TCP for local communication.
Security Advantages of Unix Domain Sockets
One of the most elegant aspects of UDS is security.
Instead of managing firewalls and port access, you rely on filesystem permissions:
chmod 660 /run/deviceiq/onvif.sock
chown deviceiq:deviceiq /run/deviceiq/onvif.sock
This makes UDS ideal for:
- Sensitive backend services
- Financial or government systems
- Edge and embedded devices
- Internal service communication
No open ports. No exposed network surface.
Real-World Scenario: Go Backend → Python ONVIF Worker
In my project, the architecture looked like this:
Go Backend → Unix Domain Socket → Python Worker → IP Camera (ONVIF)
Go Backend Responsibilities
- Accept REST API requests
- Validate input
- Build JSON messages
- Send them through the Unix socket
Python Worker Responsibilities
- Parse incoming messages
- Communicate with the camera via ONVIF
- Return structured results
Everything happens locally, inside the kernel, with minimal latency and maximum control.
Architecture Flow

Implementing a Unix Domain Socket Server in Go
listener, err := net.Listen("unix", "/run/deviceiq/onvif.sock")
Full example:
package main
import (
"encoding/json"
"fmt"
"net"
"os"
)
type Request struct {
Action string `json:"action"`
Data map[string]interface{} `json:"data"`
}
func main() {
socket := "/run/deviceiq/onvif.sock"
os.Remove(socket)
l, err := net.Listen("unix", socket)
if err != nil {
panic(err)
}
defer l.Close()
fmt.Println("Listening on", socket)
for {
conn, _ := l.Accept()
go handle(conn)
}
}
func handle(conn net.Conn) {
defer conn.Close()
var req Request
json.NewDecoder(conn).Decode(&req)
resp := map[string]interface{}{
"status": "ok",
"echo": req,
}
json.NewEncoder(conn).Encode(resp)
}
Python Worker Using Unix Domain Socket
import socket
import json
SOCKET = "/run/deviceiq/onvif.sock"
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect(SOCKET)
payload = json.dumps({
"action": "create_user",
"data": {"username": "demo"}
})
client.sendall(payload.encode())
response = client.recv(4096)
print("Response:", response.decode())
Unix Domain Socket vs TCP: A Practical Benchmark Perspective
When deciding between Unix Domain Sockets and TCP for inter-process communication, performance is often the key factor. While exact numbers depend on workload and hardware, real-world benchmarks consistently show a clear pattern.
Typical Observations
| Metric | TCP (localhost) | Unix Domain Socket |
|---|---|---|
| Latency | Higher | Lower |
| Throughput | Good | Very high |
| CPU usage | Higher | Lower |
| Context switches | More | Fewer |
| Jitter | Noticeable | Very stable |
In local IPC scenarios, Unix Domain Sockets are commonly 40–60% faster than TCP on the same machine.
Where Unix Domain Sockets Should Be Used
Use UDS When:
- Services run on the same machine
- Performance and low latency matter
- You want secure, port-free communication
- You are building monolith + worker architectures
- You are working with system-level services
Common real-world examples:
- Docker daemon
- containerd
- systemd
- internal backend workers
Where Unix Domain Sockets Should NOT Be Used
Avoid UDS When:
- Services run on different machines or VMs
- You need horizontal scaling across nodes
- You expose public APIs
- You require network-level routing or firewall rules
- Cross-platform (Windows/Linux) support is mandatory
UDS is local by design, not distributed.
The Power of Simplicity
What impressed me most about this architecture was how a complex problem—multi-language, high-frequency communication—was solved with an extremely simple tool.
No message brokers.
No HTTP overhead.
No open ports.
Just:
- One socket file
- Two processes
- JSON messages
- Kernel-level speed
Sometimes, the best engineering solutions are the simplest ones.
Conclusion
Unix Domain Sockets are one of the most effective tools for local inter-process communication:
- Fast
- Secure
- Simple
- Proven in production
If you are building systems where Go, Python, or other languages need to cooperate on the same machine—especially in camera systems, video processing, AI inference, or system tooling—Unix Domain Sockets are absolutely worth considering.
They might quietly become one of your favorite architectural decisions.