Pull & push
l pull downloads Lambda deployment packages. l push packages local files and updates $LATEST on existing functions. Authentication remains global; initial targets and region come from project configuration. The profile follows --profile, the project profile field, then default, and is displayed before the preview. The same profile is used for listing, downloading, uploading, and refreshing credentials throughout the command.
Usage
l login
l init
l pull
# Edit code in lambda/<function-name>/ and run a build if needed.
l push --dry-run
l push
Targets can be one or several function names. Without arguments or selectors, the CLI uses lambda.functionName or asks you to choose one function from the project prefix. Use function names without ARNs, aliases, or versions.
l pull billing-api
l push billing-api
l pull billing-api --region us-east-1
l pull billing-api billing-worker
l push billing-api billing-worker --dry-run
l push billing-api billing-worker
l pull --prefix billing-
l push --all
| Option | Purpose |
|---|---|
-r, --region <region> | Override the project region |
--all | All targets matching the project prefix, if configured |
-p, --prefix <prefix> | All targets with this prefix, overriding the project prefix |
--dry-run | Preview without applying changes |
-y, --yes | Skip normal confirmation; conflicts are still rejected |
Dry runs still perform AWS reads. A push dry run validates packages locally without calling the update API to validate IAM or deployment eligibility. A first push without a baseline still requires explicit approval even with --yes. Selecting a single function from a configured prefix and confirming changes require an interactive terminal; --all/--prefix do not open a function picker.
Multiple functions at once
| Selection | Pull | Push |
|---|---|---|
l pull api worker / l push api worker | Explicit names | Explicit names; local folders must exist |
--all | AWS functions in the selected region | Function folders in local lambda/ |
--prefix billing- | All AWS names starting with billing- | All local folders starting with billing- |
--all respects lambda.prefix when configured. Without a configured prefix, it covers all AWS functions for pull or all local function folders for push, even if the config only contains lambda.functionName. Use --dry-run to review targets and changes. Bulk push does not fetch its target list from AWS.
Explicit names cannot be combined with --all or --prefix. Duplicate names are processed once, in argument order. Selector targets are sorted by name. No matching targets results in an error.
All targets are checked and packages prepared before one combined confirmation. If any target fails preflight, no function code or baseline changes. Local pull conflicts with --yes also cancel the entire batch. If any push lacks a baseline, confirmation is still required with --yes. Push targets with no changes are skipped with status unchanged.
After approval, all targets are checked again before the first change, then applied sequentially with further checks per target. One project lock protects the entire operation. ZIPs are stored in the system temp directory to avoid holding every package in memory at once; these files are cleaned up when the command succeeds or fails, including dry runs.
The summary reports each function as success, failed, skipped, unchanged, preview for dry runs, or cancelled when confirmation is declined. An execution failure stops later targets and returns exit status 1. Successful functions remain applied and their state stays saved. A batch is not an atomic transaction across functions; there is no automatic AWS rollback. Inspect failed targets before retrying only the names that still need work:
l push billing-worker billing-cron --dry-run
l push billing-worker billing-cron
Folder structure
project/
├── l.config.json
├── .lignore
├── .gitignore
├── lambda/
│ └── billing-api/
│ ├── index.js
│ └── node_modules/
└── .l/
├── state.json
├── sync.lock
└── backups/
└── <function>-<timestamp>-<id>/
├── code/
└── state.json
The project root is the location of the nearest l.config.json. Commands from subfolders still read/write lambda/<name>/ at the project root. Project .l/ stores state, a temporary lock, and backups; ~/.l/ still stores global authentication. The /.l/ rule is added to .gitignore before changes are applied.
State binds the function folder to its full ARN, including account and region. Sync is rejected if the login/region points to a different identity. Use the original login/region or a separate project for another account/region.
Pull
The downloaded package contains the code deployed to AWS. It may be a bundle or binary; the original TypeScript source and repository are not always available.
The CLI verifies the package SHA-256 checksum and ZIP entry CRCs. Path traversal, duplicate or case-colliding names, symlinks, special files, and encrypted ZIPs are rejected. Packages are staged before replacing the destination folder.
The preview shows added, changed, and removed files. If local changes exist since the last sync, the CLI asks for replacement confirmation; --yes cannot bypass this conflict. The entire old folder is backed up before installing the new package. Installation/state failures trigger an attempt to restore the old folder.
Pull does not merge. Extra local files in the function folder, including files excluded by .lignore, may disappear from the active folder but remain in the backup. Cancellation leaves code and baselines unchanged, although an empty .l/ folder may remain from creating the lock.
Push
The AWS revision is compared with the last pull/push result. If it differs, push stops and asks you to retrieve and review the latest remote code. --yes does not bypass this check. A first push compares local files with the remote package and asks for target confirmation without a saved baseline.
The function folder contents go directly at the ZIP root. Before uploading, the local snapshot and remote metadata are checked again. The update request includes RevisionId to prevent remote changes between the check and upload.
The CLI shows the account, region, ARN, file changes, and ZIP size, then asks for confirmation. After upload, it waits up to 120 seconds per function and verifies the checksum and Successful status before advancing state.
Failed uploads, timeouts, or updates overwritten by another process do not advance state. AWS may have accepted the code when a connection drops; check AWS status before retrying. Invocations without a version/alias may use $LATEST. Push does not publish a version, move an alias, or change environment variables, runtime, role, memory, layers, or other function configuration.
Packaging and ignore rules
Create .lignore at the project root. Patterns follow gitignore syntax and are relative to each function's code folder:
.env
.env.*
*.test.js
tests/
coverage/
*.map
.git, .l, .lignore, and l.config.json are always excluded at every level. Runtime dependencies such as node_modules are included unless ignored. Other files, including dotfiles, may be uploaded; configure patterns as needed.
The CLI does not install dependencies, build, or run project scripts. Prepare deployment-ready output before pushing. File permissions are normalized to 0644, or 0755 for executables. Empty directories are omitted from pushed ZIPs.
Windows and executable permissions
Windows does not preserve POSIX executable bits through stat/chmod as Linux does. On Windows, the CLI preserves 0755 or 0644 from the last baseline for the same filename, including after editing its contents. A first push without a baseline takes modes from the remote package. New files use 0644.
To add or rename an executable such as bootstrap, prepare the package on Linux/WSL or macOS and set chmod +x before pushing. Do not edit .l/state.json to set permissions. On Linux/macOS, local permission changes still appear in the preview and upload package.
Limits and permissions
- Existing ZIP functions only; container images and function creation are not supported.
- Direct uploads are limited to 50 MiB compressed; S3 uploads are not supported.
- Each package is limited to 250 MiB uncompressed and 10,000 entries. AWS includes layers in its deployment limit, so final validation still happens on AWS.
- Symlinks that are not ignored are rejected, including dependency symlinks.
- No package re-signing, automatic merge, or AWS rollback.
- Pull requires
lambda:GetFunction. Push also requireslambda:UpdateFunctionCode. Pull with--all/--prefixand selecting one function from a configured prefix requirelambda:ListFunctions. Push with--all/--prefixselects local folders and does not require listing permission.
References: GetFunction, UpdateFunctionCode, and Lambda quotas.
Recovery
Local changes overwritten: compare code/ in the backup with the current code, then copy back any changes you need. Keep the latest state as the remote baseline; review the merge with l push --dry-run.
Revision conflict: back up local work, pull the latest remote, then merge changes from the backup. Do not delete state to bypass conflicts.
AWS update succeeded but state could not be saved: fix the disk problem, back up local code, then pull to align code and revision.
Unexpected process termination: check the PID and time in .l/sync.lock. Only remove a stale lock after confirming the process has stopped. Inspect backups and pull-stage-* if termination happened during folder replacement. Invalid state can be restored from an appropriate backup before syncing again.
See troubleshooting and the command reference.