How to Run Gin on Unix Sockets: Complete Guide with Examples
Gin serves HTTP over Unix domain sockets via router.RunUnix(file), which creates a net.Listener using net.Listen("unix", path), automatically manages the socket file lifecycle with defer os.Remove(file), and blocks until the server stops or encounters an error.
Running Gin on Unix sockets provides a lightweight, secure IPC mechanism for local process communication, eliminating TCP overhead and port management. The gin-gonic/gin repository provides first-class support for Unix domain sockets through the RunUnix method, available both on individual engine instances and as a package-level helper. This approach is ideal for microservices behind reverse proxies, containerized applications, or systemd socket-activated services.
How RunUnix Works Under the Hood
The Engine Implementation in gin.go
The core implementation resides in gin.go within the Engine.RunUnix method. According to the gin-gonic/gin source code, the method performs four critical operations:
- Creates the socket listener using
net.Listen("unix", file)to establish a file-based socket - Validates trusted proxy configuration using the same logic as other
Run*helpers - Logs the startup with the debug message:
Listening and serving HTTP on unix:/%s - Serves traffic by passing the listener to
http.Server.Serve(listener), which blocks until shutdown
The implementation leverages Go's standard net package to handle the Unix socket creation, ensuring compatibility with POSIX systems while maintaining Gin's familiar API surface.
Socket Lifecycle Management
Gin automatically manages the Unix socket file lifecycle to prevent stale socket accumulation. In gin.go, the method executes defer os.Remove(file) immediately after creating the listener, ensuring the socket file is deleted when RunUnix returns—whether through normal shutdown, error, or process termination. This deferred cleanup runs after server.Serve(listener) unblocks, providing automatic resource management without manual intervention.
Basic Implementation: Running Gin on a Unix Socket
To run your Gin application on a Unix domain socket, invoke RunUnix on your router instance with the absolute path to the socket file. The path must be writable by the process and should not already exist.
package main
import (
"log"
"github.com/gin-gonic/gin"
)
func main() {
// Initialize router (gin.Default(), gin.New(), or custom middleware chains)
r := gin.Default()
// Define routes as usual
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello from Unix socket!")
})
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "healthy"})
})
// Unix socket path - typically in /tmp/, /run/, or application-specific directories
const socketPath = "/tmp/gin.sock"
// Start blocking server on Unix socket
if err := r.RunUnix(socketPath); err != nil {
log.Fatalf("failed to run Gin on Unix socket: %v", err)
}
}
When this executes, Gin binds to the filesystem socket rather than a TCP port, making the application accessible only to local processes with appropriate filesystem permissions.
Alternative: Using the Package-Level Helper
For applications using the default Gin engine without custom middleware configuration, the package-level gin.RunUnix in ginS/gins.go provides a concise wrapper. This helper forwards the call to the default engine instance, reducing boilerplate for simple services.
package main
import (
"log"
"github.com/gin-gonic/gin"
)
func main() {
// Uses gin.Default() internally via ginS/gins.go
if err := gin.RunUnix("/var/run/myapp.sock"); err != nil {
log.Fatalf("server error: %v", err)
}
}
The wrapper in ginS/gins.go (line 150) simply delegates to defaultEngine.RunUnix, making it functionally equivalent to creating a default router manually but requiring less code.
Testing and Verifying Unix Socket Connections
After starting your Gin application, verify the socket creation and test HTTP communication using standard Unix tools. The socket appears as a regular file with special permissions indicating it is a socket.
# Verify the socket file exists with correct type
ls -l /tmp/gin.sock
# Output: srwxr-xr-x 1 user group 0 Jan 15 10:30 /tmp/gin.sock
# Test using curl's Unix socket support
curl --unix-socket /tmp/gin.sock http://localhost/
# Expected: Hello from Unix socket!
# Send requests to specific routes
curl --unix-socket /tmp/gin.sock http://localhost/health
For integration testing within your Go test suite, the repository provides patterns in gin_integration_test.go (line 95), demonstrating how to start temporary Unix sockets during tests and handle cleanup automatically.
Production Considerations
When deploying Gin applications with Unix sockets in production environments, address these critical operational factors:
File Permissions - The socket file inherits the umask and permissions of the creating process. Ensure the socket is readable/writable by the connecting processes (e.g., nginx, another container, or a different user). You may need to adjust permissions post-creation or run the service under specific user/group contexts.
Stale Socket Files - If the socket file already exists when RunUnix starts, net.Listen returns an error and Gin fails to start. Implement pre-flight checks to remove stale sockets from unclean shutdowns, or rely on Gin's automatic cleanup during graceful shutdowns.
Graceful Shutdown - Because Gin removes the socket file on exit through the deferred os.Remove(file) call, ensure your application handles SIGTERM and SIGINT signals properly. A clean shutdown preserves the socket cleanup behavior, while hard kills (SIGKILL) may leave stale files requiring manual removal before restart.
Systemd Integration - For systemd socket activation, ensure your service unit specifies the socket file path consistently between the systemd .socket unit and your Gin application configuration.
Summary
RunUnixingin.gocreates Unix domain sockets usingnet.Listen("unix", path)and manages the full server lifecycle- Automatic cleanup via
defer os.Remove(file)ensures socket files are deleted when the server stops - Two API entry points exist:
router.RunUnixfor custom engines andgin.RunUnixinginS/gins.gofor default configurations - Local-only access provides security benefits over TCP ports for inter-process communication on the same host
- Integration tests in
gin_integration_test.godemonstrate production-ready patterns for testing Unix socket servers
Frequently Asked Questions
What happens if the Unix socket file already exists?
Gin will fail to start with an error from the underlying net.Listen("unix", path) call. You must manually remove the existing socket file before starting the server, or ensure a previous instance performed a clean shutdown where Gin's deferred os.Remove(file) executed properly.
How do I control permissions on the created Unix socket?
The socket inherits the process umask and effective user/group. To restrict access, configure your process to run under specific user contexts or adjust umask before calling RunUnix. Unlike TCP sockets, Unix domain sockets use filesystem permissions, allowing granular access control via standard chmod and ownership changes.
Can I use Unix sockets with systemd socket activation?
Yes, though you must adapt the pattern slightly. Instead of having Gin create the socket, systemd passes the file descriptor. You would use net.Listener from systemd's socket activation and pass it to http.Server.Serve() manually, rather than using RunUnix directly, since RunUnix always creates a new listener via net.Listen.
Is there a performance difference between Unix sockets and TCP localhost?
Unix sockets typically offer 10-20% lower latency and higher throughput than TCP localhost connections because they bypass the entire TCP/IP stack, eliminating packet encapsulation, checksums, and network buffer management. For high-throughput local services, Unix sockets provide measurable efficiency gains with zero network overhead.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →