diff --git a/pkg/client/compose/caddy_test.go b/pkg/client/compose/caddy_test.go index 196bde44..d67f15ba 100644 --- a/pkg/client/compose/caddy_test.go +++ b/pkg/client/compose/caddy_test.go @@ -141,7 +141,7 @@ services: wantErr: "expected type 'string'", }, { - name: "x-caddy with x-ports conflict", + name: "x-caddy with ingress x-ports conflict", composeYAML: ` services: web: @@ -153,7 +153,26 @@ services: x-ports: - example.com:80/http `, - wantErr: "cannot specify both 'x-caddy' and 'x-ports'", + wantErr: "ingress ports in 'x-ports' and 'x-caddy' cannot be specified simultaneously", + }, + { + name: "x-caddy with host-only x-ports allowed", + composeYAML: ` +services: + web: + image: nginx + x-caddy: | + example.com { + reverse_proxy web:80 + } + x-ports: + - 8080:80@host + - 9090:90/tcp@host +`, + wantConfig: `example.com { + reverse_proxy web:80 +}`, + // Should not error - host ports are allowed with x-caddy }, } diff --git a/pkg/client/compose/service.go b/pkg/client/compose/service.go index 928e511f..bdaf0ecb 100644 --- a/pkg/client/compose/service.go +++ b/pkg/client/compose/service.go @@ -252,20 +252,26 @@ func tmpfsVolumeSpecFromCompose(serviceVolume types.ServiceVolumeConfig) api.Vol // validateServicesExtensions validates extension combinations across all services in the project. func validateServicesExtensions(project *types.Project) error { for _, service := range project.Services { - // Check for x-caddy and x-ports conflict. + // Check for x-caddy and x-ports conflict, unless all ports are host mode. hasCaddy := false if caddy, ok := service.Extensions[CaddyExtensionKey].(Caddy); ok && caddy.Config != "" { hasCaddy = true } - hasPorts := false - if ports, ok := service.Extensions[PortsExtensionKey].([]api.PortSpec); ok && len(ports) > 0 { - hasPorts = true - } - - if hasCaddy && hasPorts { - return fmt.Errorf("service '%s' cannot specify both 'x-caddy' and 'x-ports': "+ - "Caddy config is auto-generated from ports, use only one of them", service.Name) + if ports, ok := service.Extensions[PortsExtensionKey].([]api.PortSpec); ok && len(ports) > 0 && hasCaddy { + // Check if all ports are in host mode. + hasIngressPort := false + for _, p := range ports { + if p.Mode == "" || p.Mode == api.PortModeIngress { + hasIngressPort = true + break + } + } + if hasIngressPort { + return fmt.Errorf("service '%s': ingress ports in 'x-ports' and 'x-caddy' cannot be specified "+ + "simultaneously: Caddy config is auto-generated from ingress ports, use only one of them. "+ + "Host mode ports in 'x-caddy' can be used with 'x-caddy'", service.Name) + } } }