open-southeners/sidecar-local
Run Laravel Lambda functions locally with Sidecar Local, a companion package for hammerstone/sidecar. Streamline development and testing of serverless handlers so you can invoke, debug, and iterate on queues, events, and HTTP handlers on your machine before deploying to AWS.
Sidecar deploys a Laravel app to AWS Lambda and gives you Artisan commands for the full lifecycle: package, deploy, warm, and invoke locally. Version 2.0 is a local-runtime release, so most of the day-one value is in the sidecar:local loop rather than in the deploy commands themselves.
Prerequisites for 2.0
composer.json before upgrading — a stale laravel/framework constraint will block the install.sidecar:local. 2.0 runs the local runtime in Docker-in-Docker, so the daemon needs to be able to start containers (Docker Desktop, Colima, or a CI service container with a DinD sidecar).Install and configure
composer require hammerstone/sidecar
php artisan sidecar:install
The install command publishes config/sidecar.php — read this file first; it holds AWS region, credentials/role settings, packaging exclusions, and the registration point for your functions. Set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_DEFAULT_REGION (or use an instance role in CI).
Define your first function
Create a function class (one per Lambda) that declares its handler, runtime, memory, and timeout, then register it where the installer scaffolded registration. A webhook or health-check function is the best first target: it exercises packaging and invocation without needing API Gateway, queues, or a database.
First real use case
php artisan sidecar:deploy to push the function.php artisan sidecar:local --run to invoke it in the DinD runtime before touching AWS — this is the loop 2.0 is built around.If you use Sail, add --sail to sidecar:local; 2.0 mounts and networks against the Sail compose project instead of assuming a host daemon.
Local-first inner loop. With 2.0, sidecar:local --run uses the DinD topology by default. Treat it as your primary smoke test: it builds the runtime container, starts the managed sidecar-docker daemon, and invokes with a Lambda-shaped event. Keep a fixture per function (a JSON event payload) next to the function class so --run is reproducible and reviewable.
One function class per Lambda, thin and declarative. Keep handler/runtime/memory/timeout in the class and business logic in your app. Because the runtime image resolver now covers Node.js and Python AWS Lambda public ECR images, polyglot setups become viable — e.g. a Python image-resize function alongside the PHP app function — without hand-managing runtime image URIs. Pin runtime versions explicitly in the function class anyway; resolver defaults can move.
Ephemeral-by-design application code. Every invocation may land on a fresh or warm container that has no shared state with the last one. Sessions, cache, and queues must be external (Redis/SQS/S3) — never file or array drivers in the Lambda environment. Prefer stateless services and avoid static/singleton accumulation in hot paths, since warm containers reuse the process.
Deploy pipeline pattern. Run sidecar:deploy from CI, not laptops: build → deploy → warm → smoke-invoke. Add sidecar:warm after deploy when latency budgets matter; warming pre-provisions concurrency and costs money, so gate it per environment. Keep a scheduled warmer only for production-facing functions.
Cleanup as a first-class command. sidecar:local --stop now runs docker compose down --remove-orphans scoped to Sidecar-managed Compose projects. Put it in a finally block or a Make target so CI runners and dev machines don't accumulate stale function containers between branches.
Host-callback pattern. If the Lambda needs to reach something on your machine (a local MySQL, a mock API, a queue dashboard), set SIDECAR_LOCAL_HOST to the address reachable from inside the DinD network. This is the supported knob now — don't hardcode host.docker.internal in config or tests.
The 2.0 upgrade is a breaking change on two axes. Dropping Laravel 11 and PHP 8.1 means the package simply won't install on those matrices; upgrade the framework and PHP first, then Sidecar, then re-run your local invocations. Do this on a branch — function packaging can change subtly across major versions.
--run topology changed. Anything scripted against the old host-Docker-socket behavior (custom compose files, volume mounts, docker exec into a container by name, CI jobs that assumed the host daemon) needs review. The new default is DinD, so the daemon inside the runtime is separate from your host daemon.
DinD needs privileges and images. CI must provide a Docker service (or privileged runner), and the resolver pulls public ECR images on first use — expect cold pull time and possible rate limits. Cache images in CI where possible, and don't assume the network is available during the invoke.
SIDECAR_LOCAL_HOST on Linux. Docker Desktop resolves host.docker.internal automatically; on plain Linux Docker you often need --add-host=host.docker.internal:host-gateway or an explicit IP. Symptom is a connection timeout from inside the function that works fine from the host shell.
Orphan removal is scoped, but verify the scope. --stop only removes containers in Sidecar-managed Compose projects. If you've given your projects custom names, confirm they match what Sidecar expects — otherwise you'll see "stale" containers that never get cleaned.
Packaging: exclude node_modules, tests, .git, and local .env from the deploy artifact via the config's exclusion list. Never let secrets ride along in the package; inject them as Lambda environment variables. Run php artisan config:cache deliberately — a cached config baked into the artifact can mask environment-variable changes at deploy time.
Debugging locally: if --run hangs, inspect the managed sidecar-docker daemon and the function container logs before touching your app code. Most failures are image pull, host resolution, or a missing extension in the runtime — not application bugs. The package's Docker integration tests and Testbench Workbench fixtures (Node and Python hello-worlds) are the reference for a known-good environment.
Extension points worth knowing: the function class itself (runtime images, layers, memory, timeout), deployment/activation events for CI hooks and notifications, and custom packaging rules for monorepos or vendor exclusions. Reach for these before forking the package.
How can I help you explore Laravel packages today?