Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e063074
Add ReusePort config to confighttp
sinkingpoint Oct 21, 2025
4285bb8
ci fix
sinkingpoint Oct 22, 2025
1073d97
Add Validate method to ServerConfig
sinkingpoint Oct 28, 2025
77faf7e
Add ReusePort config to confighttp
sinkingpoint Oct 21, 2025
d713c54
ci fix
sinkingpoint Oct 22, 2025
6a5a94c
Add Validate method to ServerConfig
sinkingpoint Oct 28, 2025
c6f5d60
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
sinkingpoint Nov 18, 2025
ebc56b9
Merge branch 'sinkingpoint/so-reuse-port' of github.com:sinkingpoint/…
sinkingpoint Nov 18, 2025
f796cc1
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
sinkingpoint Nov 19, 2025
e428590
Merge branch 'main' into sinkingpoint/so-reuse-port
axw Nov 20, 2025
a60164e
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
sinkingpoint Nov 27, 2025
2a42a62
Merge branch 'sinkingpoint/so-reuse-port' of github.com:sinkingpoint/…
sinkingpoint Nov 27, 2025
9fcbd17
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
sinkingpoint Jan 5, 2026
92aec79
Move ReusePort to confignet
sinkingpoint Jan 5, 2026
0a4b0fb
Update changelog entry
sinkingpoint Jan 5, 2026
9e06eca
CI fixes
sinkingpoint Jan 5, 2026
dc599af
Shuffle listen_config files
sinkingpoint Jan 12, 2026
8b90b76
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
sinkingpoint Jan 12, 2026
1930e65
Merge branch 'main' of github.com:open-telemetry/opentelemetry-collec…
sinkingpoint Feb 19, 2026
1ac28b2
Document reuse-port setting more
sinkingpoint Feb 19, 2026
f5ef1d5
Fix spell check on confignet readme
sinkingpoint Feb 19, 2026
1e6b994
Update ReusePort tests to properly scope checks
sinkingpoint Feb 19, 2026
27e7524
Add SO_REUSEPORT to cspell.json
axw Apr 2, 2026
c70d0d8
Fix error assertion
axw Apr 2, 2026
1193834
Merge branch 'main' into sinkingpoint/so-reuse-port
axw Apr 2, 2026
c90e0fb
Merge branch 'main' into sinkingpoint/so-reuse-port
axw Apr 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .chloggen/so-reuse-port.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: pkg/config/confignet

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Added ReusePort option to confignet.AddrConfig to enable SO_REUSEPORT on supported platforms.

# One or more tracking issues or pull requests related to the change
issues: [14046]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
1 change: 1 addition & 0 deletions .github/workflows/utils/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"RCPC",
"Rahul",
"SASL",
"SO_REUSEPORT",
"Samplingdecision",
"Sharma",
"Statefulness",
Expand Down
1 change: 1 addition & 0 deletions config/confignet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ leverage network configuration to set connection and transport information.
- `transport`: Known protocols are "tcp", "tcp4" (IPv4-only), "tcp6"
(IPv6-only), "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
(IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and "unixpacket".
- `reuse_port`: If set to `true`, enables the SO_REUSEPORT socket option on the listener, allowing multiple processes to listen on the same port
- `dialer`: Dialer configuration
- `timeout`: Dialer timeout is the maximum amount of time a dial will wait for a connect to complete. The default is no timeout.

Expand Down
17 changes: 16 additions & 1 deletion config/confignet/confignet.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ type AddrConfig struct {

// DialerConfig contains options for connecting to an address.
DialerConfig DialerConfig `mapstructure:"dialer,omitempty"`

// ReusePort enables the SO_REUSEPORT socket option on the listener.
// This allows multiple server instances to bind to the same address/port.
// This is useful for horizontal scaling and zero-downtime restarts.
// Note: This option is only supported on Linux and Darwin-based operating systems.
ReusePort bool `mapstructure:"reuse_port,omitempty"`
Comment thread
sinkingpoint marked this conversation as resolved.

// prevent unkeyed literal initialization
_ struct{}
}
Expand All @@ -103,11 +110,19 @@ func (na *AddrConfig) Dial(ctx context.Context) (net.Conn, error) {

// Listen equivalent with net.ListenConfig's Listen for this address.
func (na *AddrConfig) Listen(ctx context.Context) (net.Listener, error) {
lc := net.ListenConfig{}
lc, err := na.getListenConfig()
if err != nil {
return nil, err
}
return lc.Listen(ctx, string(na.Transport), na.Endpoint)
}

func (na *AddrConfig) Validate() error {
_, err := na.getListenConfig()
if err != nil {
return err
}

switch na.Transport {
case TransportTypeTCP,
TransportTypeTCP4,
Expand Down
71 changes: 71 additions & 0 deletions config/confignet/confignet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"errors"
"net"
"runtime"
"testing"
"time"

Expand Down Expand Up @@ -159,3 +160,73 @@ func Test_TransportType_UnmarshalText(t *testing.T) {
err = tt.UnmarshalText([]byte("invalid"))
require.Error(t, err)
}

func TestServerReusePort(t *testing.T) {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
sc := &AddrConfig{
Endpoint: "localhost:4318",
Transport: TransportTypeTCP,
ReusePort: true,
}

_, err := sc.Listen(t.Context())
require.EqualError(t, err, "ReusePort is not supported on this platform")
return
}

tests := []struct {
name string
reusePort bool
expectedError bool
}{
{
name: "ReusePort enabled",
reusePort: true,
expectedError: false,
},
{
name: "ReusePort disabled",
reusePort: false,
expectedError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sc := &AddrConfig{
Endpoint: "localhost:4318",
Transport: TransportTypeTCP,
ReusePort: tt.reusePort,
}

ln1, err := sc.Listen(t.Context())
require.NoError(t, err)
defer ln1.Close()

ln2, err := sc.Listen(t.Context())
if tt.expectedError {
require.Error(t, err)
} else {
require.NoError(t, err)
}

if ln2 != nil {
ln2.Close()
}
})
}
}

func TestServerConfigValidate(t *testing.T) {
sc := &AddrConfig{
Endpoint: "localhost:4318",
Transport: TransportTypeTCP,
ReusePort: true,
}

if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
require.Error(t, sc.Validate())
} else {
require.NoError(t, sc.Validate())
}
}
1 change: 1 addition & 0 deletions config/confignet/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.25.0
require (
github.com/stretchr/testify v1.11.1
go.uber.org/goleak v1.3.0
golang.org/x/sys v0.39.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions config/confignet/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions config/confignet/listen_config_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//go:build !(linux || darwin)

package confignet // import "go.opentelemetry.io/collector/config/confignet"

import (
"errors"
"net"
)

func (na *AddrConfig) getListenConfig() (net.ListenConfig, error) {
if na.ReusePort {
return net.ListenConfig{}, errors.New("ReusePort is not supported on this platform")
}

return net.ListenConfig{}, nil
}
30 changes: 30 additions & 0 deletions config/confignet/listen_config_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//go:build linux || darwin
Comment thread
evan-bradley marked this conversation as resolved.

package confignet // import "go.opentelemetry.io/collector/config/confignet"

import (
"net"
"syscall"

"golang.org/x/sys/unix"
)

func (na *AddrConfig) getListenConfig() (net.ListenConfig, error) {
cfg := net.ListenConfig{}
if na.ReusePort {
cfg.Control = func(_, _ string, c syscall.RawConn) error {
var controlErr error
err := c.Control(func(fd uintptr) {
controlErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
})
if err != nil {
return err
}
return controlErr
}
}

return cfg, nil
}
Loading