Laravel Component API Reference
RemoteEnvVault
The RemoteEnvVault component manages environment variables for your Laravel application using AWS Secrets Manager. This provides a secure way to store and manage sensitive configuration values.
Large Environment Files
Environment files that exceed AWS Secrets Manager's 64KB limit are automatically split into multiple chunks. This is handled transparently by the CLI commands - you don't need to do anything special.
When pushing a large .env file:
- The file is automatically split into multiple secrets (e.g.,
/{app}/{stage}/env/1,/{app}/{stage}/env/2, etc.) - A metadata secret at
/{app}/{stage}/envtracks the chunk count - When pulling or deploying, all chunks are automatically merged back together
Constructor
new RemoteEnvVault(name: string, args?: RemoteEnvVaultArgs, opts?: ComponentResourceOptions)
RemoteEnvVaultArgs
path
- Type:
Input<string> - Default:
/{app-name}/{stage}/env - Description: The path in AWS Secrets Manager where environment variables will be stored.
Example:
const env = new RemoteEnvVault("Env", {
path: "/my-app/production/env"
});
Properties
path
- Type:
Output<string> - Description: The path in AWS Secrets Manager where environment variables are stored.
CLI Commands
Use sst-laravel env:push and sst-laravel env:pull to manage the stored variables. See the CLI reference for every command and its options.
Usage with LaravelService
const env = new RemoteEnvVault("Env");
new LaravelService("Laravel", {
vpc,
web: {
domain: "example.com"
},
reverb: {
domain: "ws.example.com"
},
config: {
environment: {
secrets: env
}
}
});
When using RemoteEnvVault, deploy your application using the sst-laravel deploy command, which will automatically fetch secrets from AWS Secrets Manager before building the Docker image:
sst-laravel deploy --stage production
LaravelService
Constructor
new LaravelService(name: string, args: LaravelArgs, opts?: ComponentResourceOptions)
Creates a new Laravel component for deploying Laravel applications to AWS Fargate.
LaravelArgs
path
- Type:
Input<string> - Default:
'.' - Description: Path to the Laravel application directory.
link
- Type:
Array<Resource | { resource: Resource; envFrom?: EnvCallback }> - Description: Resources to link to the Laravel application. Supports SST resources like databases, Redis, email services, queues, and S3 buckets. When linked, environment variables are automatically configured.
Supported resources with automatic environment variable injection:
Postgres- SetsDB_CONNECTION,DB_HOST,DB_DATABASE,DB_USERNAME,DB_PASSWORD,DB_PORTMysql- SetsDB_CONNECTION,DB_HOST,DB_DATABASE,DB_USERNAME,DB_PASSWORD,DB_PORTAurora- Sets database variables based on port (5432 for Postgres, 3306 for MySQL)- PlanetScale MySQL (Vitess)
sst.Linkable- SetsDB_CONNECTION=mysql,DB_HOST,DB_DATABASE,DB_USERNAME,DB_PASSWORD,DB_PORT, andMYSQL_ATTR_SSL_CA - PlanetScale Postgres
sst.Linkable- SetsDB_CONNECTION=pgsql,DB_HOST,DB_DATABASE,DB_USERNAME,DB_PASSWORD,DB_PORT,DB_SSLMODE=verify-full, andDB_URLwith TLS settings Redis- SetsREDIS_HOST,REDIS_PORT,REDIS_PASSWORDEmail- SetsMAIL_MAILERto 'ses'Queue- SetsSQS_QUEUEBucket- SetsFILESYSTEM_DISKto 's3',AWS_BUCKET
You can provide a custom envFrom callback function to override or extend the default environment variables:
link: [
{
resource: myDatabase,
envFrom: (resource) => ({
CUSTOM_DB_VAR: resource.host
})
}
]
(The older environment name for this callback still works but envFrom is preferred.)
PlanetScale link properties
For automatic PlanetScale injection, pass an sst.Linkable with these properties. The package exports the PlanetScaleProperties interface for this object.
| Property | Type | Required | Default |
|---|---|---|---|
provider | 'planetscale' | Yes | — |
engine | 'mysql' | 'postgres' | No | 'mysql' |
host | Input<string> | Yes | — |
database | Input<string> | Yes | — |
username | Input<string> | Yes | — |
password | Input<string> | Yes | — |
port | Input<number | string> | No | 3306 for MySQL; 5432 for Postgres |
sslCa | Input<string> | No | /etc/ssl/certs/ca-certificates.crt |
The marker and engine must be literal strings. Connection values can be plain values, promises, or Pulumi outputs. The password and Postgres connection URL remain Pulumi secrets. An incomplete marked link or an invalid engine raises an error without including credential values. For Postgres, database is the logical database name, usually postgres, not the PlanetScale project name.
MySQL uses MYSQL_ATTR_SSL_CA for certificate verification. Postgres uses DB_URL with sslmode=verify-full and sslrootcert so the TLS settings work with Laravel's configuration cache. Laravel must retain the standard MySQL SSL option or Postgres DB_URL option in config/database.php.
Postgres URL values take precedence over individual DB_* values. Change connection values in the link properties, or override the full DB_URL with an envFrom callback. Use sslCa to change the CA bundle for either engine.
Both environment file and RemoteEnvVault injection use this mapping. config.environment.autoInject: false disables it. Unmarked links are not detected as PlanetScale databases. See the PlanetScale setup examples.
permissions
- Type:
Array<{ actions: string[]; resources: string[] }> - Description: IAM permissions to grant to the Laravel application containers.
Example:
permissions: [
{
actions: ["s3:GetObject", "s3:PutObject"],
resources: ["arn:aws:s3:::my-bucket/*"]
}
]
vpc
- Type:
ClusterArgs["vpc"] - Description: VPC configuration for the ECS cluster. Inherited from SST's Cluster component.
With an sst.aws.Vpc, containers run in the private subnets when the VPC has NAT (nat: "ec2" or nat: "managed"). Without NAT, private subnets have no route to the internet, so containers could not pull their image. They run in the public subnets with a public IP instead (~$3.65/month per task). Either way, the VPC security group only accepts inbound traffic from inside the VPC, so requests still come through the load balancer.
transform
- Type:
ClusterArgs["transform"] - Description: Transform the ECS cluster, as on SST's Cluster component. The services have their own
transformunderadvanced.
transform: {
cluster: (args) => {
args.settings = [{ name: 'containerInsights', value: 'enhanced' }];
},
},
web
- Type:
LaravelWebArgs - Description: Configuration for the web service that handles HTTP traffic.
web.domain
- Type:
Input<string | { name: Input<string>; cert?: Input<string>; dns?: Input<false | Dns> }> - Description: Custom domain for the web layer. If you don't provide a domain name, you will be able to use the load balancer domain for testing (http only).
Example (simple string):
web: {
domain: "example.com"
}
Example (with stage variable):
web: {
domain: {
name: `${$app.stage}.example.com`
}
}
Example (with custom certificate):
web: {
domain: {
name: "example.com",
cert: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
}
}
Example (with custom DNS provider):
web: {
domain: {
name: "example.com",
dns: sst.cloudflare.dns()
}
}
web.size
- Type:
"small" | "medium" | "large" - Description: Simple container size. Maps to a valid Fargate cpu/memory pair:
small(0.5 vCPU / 1 GB, ~$18/mo),medium(1 vCPU / 2 GB, ~$36/mo),large(2 vCPU / 4 GB, ~$72/mo). Settingcpuormemorydirectly wins oversize.
Example:
web: {
size: "small",
scaling: { min: 1, max: 3 }
}
web.architecture
- Type:
ServiceArgs["architecture"] - Description: The CPU architecture for the web service. Prefer the simple options above; SST-specific tuning lives under
web.advanced(e.g.advanced: { architecture: "arm64" }). Setting this top-level key still works but logs a deprecation warning — useadvanced.architectureinstead.
web.cpu
- Type:
ServiceArgs["cpu"] - Description: CPU units for the web service. Wins over
web.sizewhen both are set.
web.memory
- Type:
ServiceArgs["memory"] - Description: Memory allocation for the web service. Wins over
web.sizewhen both are set.
web.storage
- Type:
ServiceArgs["storage"] - Description: Storage configuration for the web service. Setting this top-level key still works but logs a deprecation warning — use
advanced.storageinstead.
web.scaling
- Type:
ServiceArgs["scaling"] - Description: Auto-scaling configuration for the web service.
Example:
web: {
scaling: {
min: 2,
max: 10,
cpuUtilization: 70,
memoryUtilization: 80
}
}
web.logging
- Type:
ServiceArgs["logging"] - Description: Logging configuration for the web service. Setting this top-level key still works but logs a deprecation warning — use
advanced.logginginstead.
web.health
- Type:
ServiceArgs["health"] - Description: ECS container-level health check for the web service. Distinct from
web.healthCheck(load balancer URL check). Setting this top-level key still works but logs a deprecation warning — useadvanced.healthinstead.
web.healthCheck
- Type:
Input<LaravelHealthCheck> - Description: Load balancer health check applied to the default forward port (
8080/http). Shorthand so you don't have to set the fulladvanced.loadBalancerconfig just to set a path. Ignored whenadvanced.loadBalanceris provided — configure itshealthdirectly in that case.
Example:
web: {
domain: { name: 'app.example.com' },
healthCheck: {
path: '/up',
successCodes: '200',
interval: '30 seconds',
healthyThreshold: 2,
unhealthyThreshold: 3,
},
}
web.httpsRedirect
- Type:
boolean - Default:
true - Description: When a
domainis configured, redirect HTTP (port 80) traffic to the HTTPS (port 443) listener instead of forwarding it straight to the application. Set tofalseto keep forwarding HTTP traffic to the app. Has no effect when nodomainis set (there is no HTTPS listener to redirect to) or whenadvanced.loadBalanceris provided (configure itsrulesyourself in that case).
Example:
web: {
domain: { name: 'app.example.com' },
httpsRedirect: false,
}
web.accessLogs
- Type:
boolean - Default:
falsewhenweb.loadBalancer.accessLogsis enabled,trueotherwise. - Description: Stream the nginx access logs from the web container to CloudWatch. The web container runs nginx (
serversideup/php:*-fpm-nginx), which logs every request — including the load balancer health-check pings — to stdout. Set tofalseto silence those access logs (points the serversideupNGINX_ACCESS_LOGvariable at/dev/null), ortrueto keep them even with load balancer access logs enabled. An explicit value overrides the default; an ALB access-log config withenabled: falseleaves nginx logs on by default. Error logs and the Laravel application logs are unaffected. Only the web container runs nginx, so this has no effect on workers or the Reverb service.
Example:
web: {
accessLogs: false,
}
web.loadBalancer
- Type:
LaravelLoadBalancerArgs - Description: Options for the load balancer in front of the web service:
sslPolicy,ingressCidrs, andaccessLogs. The load balancer is hardened by default: its HTTPS listeners only accept TLS 1.2 and 1.3, its security group only opens the ports it listens on, and it drops HTTP headers with an invalid name. You only need this to go further (an IP allowlist, access logs) or to pick another policy. The SST load balancer config (rules,domain,health, ...) does not go here. Setweb.advanced.loadBalancerfor that. Any other key fails the deploy. See Load Balancer.
Example:
web: {
domain: 'app.example.com',
loadBalancer: {
ingressCidrs: ['173.245.48.0/20', '2400:cb00::/32'],
accessLogs: true,
},
}
web.loadBalancer.sslPolicy
- Type:
Input<string> - Default:
"ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09" - Description: SSL security policy for the HTTPS/TLS listeners of the load balancer. The default is the policy AWS recommends, which accepts TLS 1.2 and 1.3 only. Plain HTTP listeners reject an SSL policy, so the package leaves them untouched. See TLS policy.
Example:
web: {
domain: 'app.example.com',
loadBalancer: {
sslPolicy: 'ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06',
},
}
web.loadBalancer.ingressCidrs
- Type:
Input<string[]> | { v4?: Input<string[]>; v6?: Input<string[]>; ports?: Input<number[]> } - Default:
["0.0.0.0/0"] - Description: Only accept traffic to the load balancer from these CIDR blocks, for example the edge ranges of the CDN or WAF in front of it, so nobody can go around it by calling the load balancer address directly. By default the load balancer accepts traffic from everywhere, but only on the ports it listens on (SST opens every port and protocol). Pass a list (IPv4 and IPv6 blocks are told apart for you), or an object with
v4,v6, andports.portsdefaults to the ports the load balancer listens on (80, plus443when a domain is set), and is only needed whenadvanced.loadBalanceris a value the package cannot read before the deploy, in which case80and443are used. See IP allowlist.
Example (list):
web: {
loadBalancer: {
ingressCidrs: ['173.245.48.0/20', '103.21.244.0/22', '2400:cb00::/32'],
},
}
Example (object):
web: {
loadBalancer: {
ingressCidrs: {
v4: ['173.245.48.0/20', '103.21.244.0/22'],
v6: ['2400:cb00::/32'],
},
},
}
web.loadBalancer.accessLogs
- Type:
boolean | { bucket?: Input<string> | { name: Input<string> }; prefix?: Input<string>; enabled?: Input<boolean>; retentionDays?: number | false } - Default:
false - Description: Ship the load balancer access logs to an S3 bucket. Set to
trueto let the package create the bucket, or pass an object to choose the bucket, prefix, and retention. Enabling this onwebdisables nginx access logs by default; setweb.accessLogs: trueto keep both. Error logs and Laravel application logs are unaffected. Off by default, since it creates a bucket and adds storage cost to every stage. See Access logs in S3.bucket: An existing S3 bucket to deliver the logs to: ansst.aws.Bucketor a bucket name. When omitted, the package creates a dedicated bucket with public access blocked and the Elastic Load Balancing log-delivery policy attached. It uses the S3 default encryption (SSE-S3), since ELB cannot deliver logs to a bucket encrypted with a KMS key. When you bring your own bucket, you own its bucket policy. The package does not attach one, because a bucket can only have a single policy.prefix: S3 key prefix the logs are delivered under. Leading and trailing slashes are stripped, since ELB rejects them. The prefix must not include the reservedAWSLogspath segment.enabled: Whether the load balancer ships access logs. Set tofalseto stop shipping logs while keeping the bucket and the logs already in it. Defaults totrue.retentionDays: Days to keep access logs before they expire. Set tofalseto keep them forever. Only used when the package creates the bucket. Defaults to90.
Example:
web: {
loadBalancer: {
accessLogs: true,
},
}
Example (with options):
web: {
loadBalancer: {
accessLogs: {
prefix: 'alb',
retentionDays: 365,
},
},
}
web.horizon
- Type:
Input<boolean> - Default:
false - Description: Run Laravel Horizon (
php artisan horizon) as a supervised background process inside the web container, alongside nginx and PHP-FPM. If the process crashes, s6 restarts it in place without interrupting HTTP traffic.
web.scheduler
- Type:
Input<boolean> - Default:
false - Description: Run the Laravel scheduler as a supervised background process inside the web container:
php artisan schedule:runat the start of every minute, likeschedule:work, but with the output and errors of the tasks apart, so the JSON log lines of a task stay whole. If the web service scales beyond one container, every replica runs the scheduler — useonOneServer()with a shared cache store on your scheduled jobs.
web.tasks
- Type:
Input<{ [key: string]: Input<{ command: Input<string>; dependencies?: Input<string[]> }> }> - Description: Custom long-running commands supervised inside the web container, keyed by task name.
dependencieslists services that must start first: other tasks of the same container,base(s6-overlay's setup, which every task depends on anyway),nginx, andphp-fpm. A dependency on anything else fails the deploy before anything is built.
Example:
web: {
horizon: true,
scheduler: true,
tasks: {
pulse: {
command: "php artisan pulse:work"
}
}
}
web.executionRole
- Type:
ServiceArgs["executionRole"] - Description: Execution role for the web service. Setting this top-level key still works but logs a deprecation warning — use
advanced.executionRoleinstead.
web.permissions
- Type:
ServiceArgs["permissions"] - Description: IAM permissions specific to the web service. Falls back to the top-level
permissionswhen not set.
web.advanced
- Type:
LaravelAdvancedArgs - Description: Escape hatch for SST experts.
architecture,storage,logging,health,executionRole,loadBalancer, andtransformpassed straight to the underlyingsst.aws.Service. Values here win over the deprecated top-level keys.advanced.loadBalanceris the SST load balancer config (rules,domain,health, ...) and replaces the load balancer the package sets up; it is not the same asweb.loadBalancer.
Example:
web: {
advanced: {
architecture: "arm64",
}
}
workers
- Type:
LaravelWorkerConfig[] - Description: Configuration for worker services (Horizon, scheduler, or custom tasks).
workers[].name
- Type:
Input<string> - Description: Name of the worker service. If not provided, defaults to
worker-{index}.
workers[].horizon
- Type:
Input<boolean> - Default:
false - Description: Run Laravel Horizon (
php artisan horizon). When it exits, the container stops with its exit code and ECS replaces the task.
workers[].scheduler
- Type:
Input<boolean> - Default:
false - Description: Run the Laravel scheduler:
php artisan schedule:runat the start of every minute, likeschedule:work, but with the output and errors of the tasks apart, so the JSON log lines of a task stay whole. When it exits, the container stops with its exit code and ECS replaces the task.
workers[].tasks
- Type:
Input<{ [key: string]: Input<{ command: Input<string>; dependencies?: Input<string[]> }> }> - Description: Custom long-running commands, keyed by task name. s6 restarts them in place when they exit.
dependencieslists services that must start first: other tasks of the same worker (includinglaravel-horizonandlaravel-schedulerwhen enabled), orbase. A dependency on anything else fails the deploy before anything is built.
Example:
workers: [
{
name: "main-worker",
horizon: true,
scheduler: true,
scaling: {
min: 1,
max: 5
}
},
{
name: "custom-worker",
horizon: true,
tasks: {
"my-task": {
command: "php artisan my:command",
dependencies: ["laravel-horizon"]
}
}
}
]
workers[].architecture
- Type:
ServiceArgs["architecture"] - Description: The CPU architecture for the worker service. Prefer
advanced.architecture— the top-level key still works but logs a deprecation warning.
workers[].size
- Type:
"small" | "medium" | "large" - Description: Simple container size (same mapping as
web.size). Settingcpuormemorydirectly wins oversize.
workers[].cpu
- Type:
ServiceArgs["cpu"] - Description: CPU units for the worker service. Wins over
workers[].sizewhen both are set.
workers[].memory
- Type:
ServiceArgs["memory"] - Description: Memory allocation for the worker service. Wins over
workers[].sizewhen both are set.
workers[].storage
- Type:
ServiceArgs["storage"] - Description: Storage configuration for the worker service. Prefer
advanced.storage— the top-level key still works but logs a deprecation warning.
workers[].scaling
- Type:
ServiceArgs["scaling"] - Description: Auto-scaling configuration for the worker service.
workers[].logging
- Type:
ServiceArgs["logging"] - Description: Logging configuration for the worker service. Prefer
advanced.logging— the top-level key still works but logs a deprecation warning.
workers[].health
- Type:
ServiceArgs["health"] - Description: Health check configuration for the worker service. Prefer
advanced.health— the top-level key still works but logs a deprecation warning.
workers[].executionRole
- Type:
ServiceArgs["executionRole"] - Description: Execution role for the worker service. Prefer
advanced.executionRole— the top-level key still works but logs a deprecation warning.
workers[].permissions
- Type:
ServiceArgs["permissions"] - Description: IAM permissions specific to this worker. Falls back to the top-level
permissionswhen not set.
workers[].loadBalancer
- Type:
LaravelLoadBalancerArgs - Description: Same options and defaults as
web.loadBalancer(sslPolicy,ingressCidrs,accessLogs). Workers have no load balancer, so this only applies to a worker withadvanced.loadBalancer. It is ignored with a warning otherwise.
workers[].advanced
- Type:
LaravelAdvancedArgs - Description: Escape hatch for SST experts. Same shape as
web.advanced.
reverb
- Type:
boolean | LaravelReverbArgs - Default:
false - Description: Configuration for a dedicated Laravel Reverb service. When enabled, SST Laravel creates a worker-style service that runs
php artisan reverb:startand exposes it through a load balancer.
Example:
reverb: {
domain: "ws.example.com",
scaling: {
min: 1,
max: 2
}
}
You can also enable Reverb with defaults:
reverb: true
reverb.domain
- Type:
Input<string | { name: Input<string>; cert?: Input<string>; dns?: Input<false | Dns> }> - Description: Custom domain for the Reverb service. If provided, SST Laravel routes HTTP and HTTPS traffic to Reverb's internal listener on port 8080 by default.
Example (with custom DNS provider):
reverb: {
domain: {
name: "ws.example.com",
dns: sst.cloudflare.dns()
}
}
When reverb.domain is configured, SST Laravel auto-injects:
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080
REVERB_HOST=ws.example.com
REVERB_PORT=443
REVERB_SCHEME=https
reverb.host
- Type:
string - Default:
"0.0.0.0" - Description: Host the Reverb server listens on inside the container.
reverb.port
- Type:
number - Default:
8080 - Description: Port the Reverb server listens on inside the container. The default load balancer forwards traffic to this port.
reverb.command
- Type:
string - Default:
"php artisan reverb:start" - Description: Command used to start the Reverb service.
reverb.architecture
- Type:
ServiceArgs["architecture"] - Description: The CPU architecture for the Reverb service. Prefer
advanced.architecture— the top-level key still works but logs a deprecation warning.
reverb.size
- Type:
"small" | "medium" | "large" - Description: Simple container size (same mapping as
web.size). Settingcpuormemorydirectly wins oversize.
reverb.cpu
- Type:
ServiceArgs["cpu"] - Description: CPU units for the Reverb service. Wins over
reverb.sizewhen both are set.
reverb.memory
- Type:
ServiceArgs["memory"] - Description: Memory allocation for the Reverb service. Wins over
reverb.sizewhen both are set.
reverb.storage
- Type:
ServiceArgs["storage"] - Description: Storage configuration for the Reverb service. Prefer
advanced.storage— the top-level key still works but logs a deprecation warning.
reverb.scaling
- Type:
ServiceArgs["scaling"] - Description: Auto-scaling configuration for the Reverb service. Horizontal Reverb scaling requires Redis and
REVERB_SCALING_ENABLED=truein your Laravel environment.
reverb.logging
- Type:
ServiceArgs["logging"] - Description: Logging configuration for the Reverb service. Prefer
advanced.logging— the top-level key still works but logs a deprecation warning.
reverb.health
- Type:
ServiceArgs["health"] - Description: ECS health check configuration for the Reverb service. Prefer
advanced.health— the top-level key still works but logs a deprecation warning.
reverb.executionRole
- Type:
ServiceArgs["executionRole"] - Description: Execution role for the Reverb service. Prefer
advanced.executionRole— the top-level key still works but logs a deprecation warning.
reverb.permissions
- Type:
ServiceArgs["permissions"] - Description: IAM permissions specific to the Reverb service. Falls back to the top-level
permissionswhen not set.
reverb.loadBalancer
- Type:
LaravelLoadBalancerArgs - Description: Options for the Reverb load balancer. Same options and defaults as
web.loadBalancer(sslPolicy,ingressCidrs,accessLogs).
Example:
reverb: {
domain: 'ws.example.com',
loadBalancer: {
ingressCidrs: ['173.245.48.0/20', '2400:cb00::/32'],
},
}
reverb.advanced
- Type:
LaravelAdvancedArgs - Description: Escape hatch for SST experts. Same shape as
web.advanced.
ecsEvents
- Type:
boolean - Default:
true - Description: Record the cluster's ECS events in CloudWatch Logs: every task that stops (services' and standalone tasks', such as task runners), with its stop code, reason, and exit codes, plus deployments and service actions. ECS lists stopped tasks for about an hour only; with this,
status --since 24hreports restarts and crashes over the whole window. Creates a small Lambda function, two EventBridge rules, and a log group (/sst/cluster/<cluster>/sst-laravel/ecs-events, kept 30 days). It runs once per event, which costs next to nothing. Not created insst dev.
new LaravelService('MyApp', {
ecsEvents: false,
web: {},
});
For a cluster that LaravelService didn't create, such as an sst.aws.Cluster for task runners, EcsEventRecorder creates the same resources. Its cluster takes an sst.aws.Cluster or an aws.ecs.Cluster ({ arn, name }), and logGroupName is the log group of the events:
import { EcsEventRecorder } from '@kirschbaum-development/sst-laravel';
const tasks = new sst.aws.Cluster('Tasks', { vpc });
new EcsEventRecorder('TasksEvents', { cluster: tasks });
config
- Type:
object - Description: Config settings.
config.php
- Type:
Input<number> - Default:
8.4 - Description: PHP version. Available versions: 8.1, 8.2, 8.3, 8.4, 8.5
config.phpExtensions
- Type:
Input<string[]> - Default:
[] - Description: Additional PHP extension names to install in every web, worker, and Reverb image. Keeps the extensions in the base image and the package's
gdandintl. Installed with ServerSideUp'sinstall-php-extensionsbeforeconfig.buildCommands. Accepts names such asimagickandpdo_pgsql, one per entry; version constraints, URLs, and installer flags are not supported. Duplicate names are ignored. Failed installation fails the image build.
config: {
phpExtensions: ['imagick', 'bcmath'],
},
config.buildCommands
- Type:
Input<string[]> - Default:
[] - Description: Shell commands to customize every web, worker, and Reverb image. Runs as root after PHP extensions are installed, before the application and generated environment are copied. Each entry runs in its own
/bin/sh -c, in array order. A nonzero exit stops the build; use&&within an entry to stop if an intermediate command fails. Files persist between entries; working-directory changes and exports do not. Commands share one cached Docker layer. Multiline scripts are supported.
Supply commands without the RUN prefix. Dockerfile instructions (COPY, ENV, etc.) and RUN flags are not supported. Use absolute paths for files you create. Application files, the generated .env, linked-resource variables, and config.environment.vars are unavailable at this stage. Containers still run as www-data. Keep secrets out of commands: build arguments can appear in image metadata and build logs. For application commands at startup, use config.deployment.script. See Customizing the images.
config: {
buildCommands: [
'apt-get update && apt-get install -y --no-install-recommends poppler-utils && rm -rf /var/lib/apt/lists/*',
],
},
config.opcache
- Type:
Input<boolean> - Default:
true - Description: PHP Opcache should be enabled?
config.logFormat
- Type:
'json' | 'text' - Default:
'json' - Description: How Laravel's
stderrlog channel writes to CloudWatch. SST Laravel setsLOG_STDERR_FORMATTERto match, unless the environment sets it.json: one JSON line per log entry, with Monolog's JSON formatter, so each entry is one CloudWatch event, and Logs Insights can read its fields (logs:errors). The formatter leaves the stack trace out.text: Laravel's default lines; a stack trace arrives as one CloudWatch event per line.
See Monitoring.
config: {
logFormat: 'text',
},
config.environment
- Type:
object - Description: Environment variable configuration.
config.environment.file
- Type:
Input<string> - Description: Use this option if you want to import an .env file during build. By default, SST Laravel won't use your .env file since that might be the wrong file when deploying from your local machine.
Example:
config: {
environment: {
file: `.env.${$app.stage}`
}
}
config.environment.autoInject
- Type:
Input<boolean> - Default:
true - Description: Set this to false in case you don't want to auto inject environment variables from your linked resources.
config.environment.vars
- Type:
FunctionArgs["environment"] - Description: Custom environment variables that will be automatically injected into your application.
Example:
config: {
environment: {
vars: {
SESSION_DRIVER: 'redis',
QUEUE_CONNECTION: 'redis',
LOG_CHANNEL: 'stderr'
}
}
}
config.environment.secrets
- Type:
RemoteEnvVault - Description: Use a
RemoteEnvVaultcomponent to manage environment variables in AWS Secrets Manager. When provided, secrets will be fetched from AWS Secrets Manager at build time using thesst-laravel deploycommand.
Example:
const env = new RemoteEnvVault("Env");
new LaravelService("Laravel", {
config: {
environment: {
secrets: env
}
}
});
Note: When using
secrets, you should deploy usingsst-laravel deploy --stage <stage>instead ofsst deploydirectly. This ensures secrets are fetched from AWS Secrets Manager before the Docker build.
config.environment.inImage
- Type:
boolean - Default:
false - Description: Where the containers get the variables of
fileorsecretsfrom. By default the package uploads them to a private S3 bucket, and ECS sets them when a container starts, so no secret is stored in the container image. Only the services can read the bucket. The container also writes them to the app's.envwhen it starts. Set totrueto copy the.envinto the image instead, the way versions before 0.7.4 did. Anyone who can pull the image can read it. This option will be removed in a future version. See How the containers get the variables.
config.environment.kms
- Type:
boolean - Default:
false - Description: Encrypt the environment with a customer-managed KMS key that only the services can decrypt, so even a principal with access to the bucket can't read it. Costs about $1 a month per key, and every decryption is logged in CloudTrail. Not used with
inImage.
Example:
config: {
environment: {
file: `.env.${$app.stage}`,
kms: true
}
}
config.deployment
- Type:
object - Description: Custom deployment configurations.
config.deployment.script
- Type:
Input<string> - Description: Path to a custom deployment script to run during container startup.
Example:
config: {
deployment: {
script: "./deploy.sh"
}
}
Properties
url
- Type:
Output<string> | undefined - Description: The URL of the web service. If
web.domainis set, returns the custom domain URL. Otherwise, returns the auto-generated load balancer URL.undefinedwhen nowebservice is configured.
Example:
const app = new LaravelService("MyApp", { ... });
console.log(app.url); // https://example.com or https://xyz.elb.amazonaws.com
reverbUrl
- Type:
Output<string> | undefined - Description: The URL of the Reverb service. If
reverb.domainis set, returns the custom domain URL. Otherwise, returns the auto-generated load balancer URL.undefinedwhen noreverbservice is configured.
Example:
const app = new LaravelService("MyApp", { ... });
console.log(app.reverbUrl); // https://ws.example.com or https://xyz.elb.amazonaws.com
deployment
- Type:
LaravelDeployment - Description: Expected deployment metadata for
deployandstatus: schema version, app and stage, and the exact cluster, ECS service, and task definition ARNs for web, workers, and Reverb. Return it asdeployment: app.deploymentfromrun(); use an array for multiple components. In dev mode the service list is empty. AddtaskDefinitions: [taskRunner.taskDefinition]to the returned object to opt an external task into image availability checks. See deployment verification.
nodes
- Type:
{ cluster: sst.aws.Cluster; web?: sst.aws.Service; reverb?: sst.aws.Service; workers: Record<string, sst.aws.Service> } - Description: The underlying resources: the ECS cluster and the
sst.aws.Serviceof web, Reverb, and each worker (by worker name). Use them to add alarms, permissions, or outputs of your own. Resource options passed toLaravelService(such asproviderorprotect) apply to all of them.
Example:
const app = new LaravelService("MyApp", { ... });
return {
queue: app.nodes.workers.queue.service,
};
Complete Example
const vpc = new sst.aws.Vpc("MyVpc");
const database = new sst.aws.Postgres("MyDatabase", { vpc });
const redis = new sst.aws.Redis("MyRedis", { vpc });
const bucket = new sst.aws.Bucket("MyBucket");
const app = new LaravelService("MyApp", {
path: "./",
vpc,
link: [database, redis, bucket],
permissions: [
{
actions: ["s3:*"],
resources: [bucket.arn, `${bucket.arn}/*`]
}
],
web: {
domain: "example.com",
scaling: {
min: 2,
max: 10
}
},
reverb: {
domain: "ws.example.com",
scaling: {
min: 1,
max: 2
}
},
workers: [
{
name: "queue-worker",
horizon: true,
scheduler: true,
scaling: {
min: 1,
max: 5
}
}
],
config: {
php: 8.4,
opcache: true,
environment: {
file: `.env.${$app.stage}`,
autoInject: true,
vars: {
SESSION_DRIVER: 'redis',
QUEUE_CONNECTION: 'redis'
}
},
deployment: {
script: "./deploy.sh"
}
}
});
return {
url: app.url,
deployment: app.deployment,
reverbUrl: app.reverbUrl
};
Example with RemoteEnvVault (Secrets Manager)
const vpc = new sst.aws.Vpc("MyVpc");
const database = new sst.aws.Postgres("MyDatabase", { vpc });
const redis = new sst.aws.Redis("MyRedis", { vpc });
// Create environment secrets manager
const env = new RemoteEnvVault("Env");
const app = new LaravelService("MyApp", {
path: "./",
vpc,
link: [database, redis],
web: {
domain: "example.com",
scaling: {
min: 2,
max: 10
}
},
reverb: {
domain: "ws.example.com"
},
workers: [
{
name: "queue-worker",
horizon: true,
scheduler: true
}
],
config: {
php: 8.4,
environment: {
// Use secrets from AWS Secrets Manager
secrets: env,
// Auto-inject linked resource variables (database, redis)
autoInject: true,
// Additional runtime variables
vars: {
SESSION_DRIVER: 'redis',
QUEUE_CONNECTION: 'redis'
}
}
}
});
return {
url: app.url,
deployment: app.deployment,
reverbUrl: app.reverbUrl,
secretsPath: env.path
};
Workflow with RemoteEnvVault
-
Initial setup - Push your
.envfile to AWS Secrets Manager:sst-laravel env:push --stage production --input .env.production -
Deploy - Use the sst-laravel CLI to deploy (automatically fetches secrets):
sst-laravel deploy --stage production -
Update secrets - When you need to update environment variables:
# Pull current secrets (creates .env.production by default)sst-laravel env:pull --stage production# Edit the filenano .env.production# Push updated secretssst-laravel env:push --stage production --input .env.production# Redeploy to apply changessst-laravel deploy --stage production