Motivation
Consolidating the web + Reverb services onto one shared sst.aws.Alb (to cut the per-ALB fixed cost) currently requires bypassing the package's defaults, hand-copying them into the app's sst.config.ts, and working around an SST condition-schema gap. Concrete integration: kirschbaum-development/sherpa#668.
What the app config is forced to do today
Passing loadBalancer: { instance: sharedAlb, ... } on web/reverb replaces the package-built load balancer config wholesale, which means:
- Reverb's default health check is lost — the
/apps + successCodes: '200-499' probe that addReverbService hard-codes (laravel-sst.ts reverbConfig.loadBalancer ?? { ... health ... }) must be re-declared by hand in the app config, and will silently drift if the package's default ever changes.
httpsRedirect is lost — the 80→443 redirect the package normally emits via buildDefaultPublicPorts has to be reconstructed as a raw defaultActions override in the Alb's listener transform (the standalone Alb component's listeners default to a fixed 403 and expose no redirect shorthand).
- Host-based routing needs a workaround — SST's ALB-attachment rule conditions only support
path/query/header, and AWS rejects matching Host via an http-header condition (a host-header condition is required). The app config must declare a placeholder header condition (to satisfy SST's at-least-one-condition validation) and then replace it via transform.listenerRule with { hostHeader: { values: [...] } }.
- Rule priorities are coordinated by convention — each attached service picks a listener-rule priority that must be globally unique on the shared ALB, with no central place declaring them.
domain decouples from routing — the service domain arg is still needed (it feeds APP_URL/REVERB_* env vars) but no longer drives cert/DNS/routing, which is surprising to read.
Proposal
A first-class shared-ALB option so the wrapper keeps owning its defaults, e.g.:
new LaravelService('LaravelApp', {
alb: sharedAlb, // or per-service: web.alb / reverb.alb
web: { domain: 'app.example.com', albRulePriority: 20 },
reverb: { domain: 'reverb.example.com', albRulePriority: 10 },
});
Where the package would, per public service:
- create the target-group attachment with its own default health check (web default, Reverb
/apps 200-499),
- emit the correct
hostHeader listener-rule condition derived from domain (encapsulating the SST http-header-vs-host-header workaround, or upstreaming a host condition to SST),
- validate priority uniqueness across the services it manages,
- optionally install the 80→443 redirect on the shared ALB's HTTP listener so
httpsRedirect keeps meaning something in shared mode.
Notes
- SST's
Service already supports loadBalancer: { instance: Alb } attachment and transform.listenerRule, so this is composition the wrapper can do without SST changes; a cleaner long-term fix is adding a host condition upstream in SST's ServiceAlbRule.
- Happy to contribute the PR — the working end-state config this would replace is in kirschbaum-development/sherpa#668.
Motivation
Consolidating the web + Reverb services onto one shared
sst.aws.Alb(to cut the per-ALB fixed cost) currently requires bypassing the package's defaults, hand-copying them into the app'ssst.config.ts, and working around an SST condition-schema gap. Concrete integration: kirschbaum-development/sherpa#668.What the app config is forced to do today
Passing
loadBalancer: { instance: sharedAlb, ... }onweb/reverbreplaces the package-built load balancer config wholesale, which means:/apps+successCodes: '200-499'probe thataddReverbServicehard-codes (laravel-sst.tsreverbConfig.loadBalancer ?? { ... health ... }) must be re-declared by hand in the app config, and will silently drift if the package's default ever changes.httpsRedirectis lost — the 80→443 redirect the package normally emits viabuildDefaultPublicPortshas to be reconstructed as a rawdefaultActionsoverride in the Alb's listener transform (the standaloneAlbcomponent's listeners default to a fixed 403 and expose no redirect shorthand).path/query/header, and AWS rejects matchingHostvia anhttp-headercondition (ahost-headercondition is required). The app config must declare a placeholderheadercondition (to satisfy SST's at-least-one-condition validation) and then replace it viatransform.listenerRulewith{ hostHeader: { values: [...] } }.domaindecouples from routing — the servicedomainarg is still needed (it feedsAPP_URL/REVERB_*env vars) but no longer drives cert/DNS/routing, which is surprising to read.Proposal
A first-class shared-ALB option so the wrapper keeps owning its defaults, e.g.:
Where the package would, per public service:
/apps200-499),hostHeaderlistener-rule condition derived fromdomain(encapsulating the SSThttp-header-vs-host-headerworkaround, or upstreaming ahostcondition to SST),httpsRedirectkeeps meaning something in shared mode.Notes
Servicealready supportsloadBalancer: { instance: Alb }attachment andtransform.listenerRule, so this is composition the wrapper can do without SST changes; a cleaner long-term fix is adding ahostcondition upstream in SST'sServiceAlbRule.