- Optional
preserve_directory_symlinks(defaultfalse, Linux and macOS). A symlink to a directory in the revision is installed as a symlink instead of copying the directory it points to (#152). The AppSpecpermissionssection is not applied through a preserved link; a warning is logged instead.
- Lifecycle event results are retried on transient errors. When reporting a result fails with a 5xx or network error, the agent retries with exponential backoff for up to 65 minutes after the command was acknowledged. Previously only throttling was retried, so a hook that succeeded could fail with "CodeDeploy agent was not able to notify CodeDeploy server the execution result of this lifecycle event". Stopping the agent during retries can take as long as the retry period, bounded by
kill_agent_max_wait_time_seconds. - Symlinks replace existing files and symlinks (#143). Installing a symlink over an existing file or symlink now replaces it. A symlink over an existing directory is refused before any file is copied, and cleanup removes dangling symlinks.
- File names that are not valid UTF-8 (#284). Such files no longer hang the Install event; they are installed and removed on the next deployment when a revision drops them. A destination path that contains a line break fails the deployment before any files are copied.
- Concurrent deployments that create the same missing parent directory (#429) no longer fail the Install event.
- GitHub deployments that use an access token (#434) no longer fail with HTTP 401. The token is now redacted only in the debug log.
- On Windows, agent deployment folders stay readable by standard users (
deployment-logs,ongoing-deployment, and the per-deployment folders), matching the 0755 folders on Linux and macOS. Standard users cannot modify them.
- Improved file system permissions on the agent installation folder on Windows Server.
- macOS support on EC2 Mac, macOS 15 and later (Apple silicon, aarch64). The agent ships as a
.pkgand runs as a launchd LaunchDaemon. Deployments use AppSpecos: linux. AppSpecpermissionsentries that setaclsorcontextare rejected before the install runs;owner,groupandmodeapply. Agent self-update installs the.pkgon macOS. - Optional parallel S3 bundle downloads. Setting
s3_download_max_concurrencyabove 1 (max 64) downloads a bundle in parallel HTTP range requests ofs3_download_chunk_sizebytes (default 8MiB, minimum 64KiB). Every range is pinned to the object's ETag, or to its version when one is set, so an object replaced mid-download fails the deployment. The default of 1 keeps the single-request download.
- Deployment log format matches the 1.x agent (#430). Each
scripts.logline is now written tocodedeploy-agent-deployments.logas it is produced, as[<timestamp>] [<deployment_id>]<message>. Previously the agent copied the last 2048 bytes of script output into the log after each lifecycle event, which dropped the deployment ID, doubled the timestamp, added a blank line after each record, and lostLifecycleEventheaders and any older output. The failure-summary line is removed, and blank script output lines are logged as a bare[stdout]record in both files. - Command endpoints in the European Sovereign Cloud and ISO partitions. Hosts in
eusc-*,us-iso-*,us-isob-*,eu-isoe-*andus-isof-*regions resolved anamazonaws.comendpoint that does not exist. Each partition now uses its own DNS suffix.
- (opt-in) On macOS,
strip_loader_env_in_hooksstrips theDYLD_*loader variables from hook environments.
RESTARTdeployment mode. The agent installs from the copy of the revision from the deployment group's last successful deployment already on the instance. If that copy is missing or incomplete, the agent downloads the revision.
- A deployment whose AppSpec file path resolves outside the application revision directory is rejected. Nested paths such as
configs/appspec.ymlare still supported.
- Windows AppSpec paths with leading separators. A
files.sourceorhooks.locationbeginning with\(including the bare\form) or/resolved against the drive root instead of the deployment archive, failing Install events with access-denied errors. Leading separators are now stripped.
Version 2.0.0 replaces the Ruby runtime with a single statically linked binary in Rust, completely removing the dependency on a Ruby runtime.
The rewrite is behavior-compatible by default. Switching from v1.8.x to v2.0.0 is not expected to affect deployment outcomes, and all behaviors match the previous agent unless explicitly noted below. The new agent also introduces a set of opt-in configuration flags that let you tighten its security posture to match your environment's requirements.
- Single native binary. The 3-layer v1 entry point (version-detection shim, GLI CLI, SysV init.d script) is replaced by one executable with a
clapCLI (start/stop/restart/status/deploy-local). - Local command port. A loopback-only (127.0.0.1) TCP management interface exposing
ping,status, andinjectcommands. Disabled by default (enable_command_port). Protected by a 256-bit CSPRNG token in a0600/ SYSTEM+Administrators-only discovery file with constant-time comparison, an 8-connection cap, a 30s idle timeout, and a 64 KiB request limit. - Native Windows service integration. Service install/uninstall/run via the Service Control Manager, replacing the
win32-daemongem and Ocra toolchain. ReportsStartPendingthenRunningso the SCM no longer races slow startup (error 1053), auto-detects whether it was launched by the SCM or a console, validates that the service binary lives under the expected install prefix, and stops the service before deleting it on uninstall. - Custom WiX MSI manifest that installs to
C:\ProgramData\Amazon\CodeDeploy\and registers the service against the correct entry point. - Concurrent deployments. After dispatching a command, and if capacity remains, the agent re-polls immediately instead of waiting the full
wait_between_runsinterval (default 30s), enabling back-to-back command pickup during multiple deployments. - In-flight command de-duplication. A command the service re-delivers while the agent is still processing it is skipped rather than run twice, with a bounded consecutive-skip cap before the agent falls back to its normal poll interval.
- Process-wide throttle circuit breaker. A shared backoff gate coordinates all worker threads on a single deadline when the service returns HTTP 429 (or non-429 throttling signals), and retries
PutHostCommandCompleterather than dropping it. - Richer service-error diagnostics. Non-2xx responses preserve the service
__typeand response body in agent logs instead of collapsing to a bareHTTP <status>. - Startup TLS pre-check that fails fast on a broken endpoint/certificate before entering the poll loop.
- Structured per-hook audit log recording source user, target user (
runas),sudo, and script path for every lifecycle hook spawn. - A
.versionfile written next to the install root at service startup (mode0444; protected DACL on Windows), reporting the running agent version. s3://and GitHub bundle sources fordeploy-local. GitHub browser URLs (https://github.com/<org>/<repo>) and API URLs (https://api.github.com/repos/<org>/<repo>/{zipball,tarball}/<ref>) are both accepted. Private repositories use--github-token, and GitHub downloads honorproxy_uri.- New configuration keys (all defaulting to v1-compatible behavior):
enable_command_port,reject_symlinks_in_bundle,reject_path_traversal_in_bundle,reject_unsafe_permissions_in_bundle,reject_unconfined_selinux_in_bundle,reject_symlink_permission_targets,ignore_ownership_in_bundle,restrict_agent_dir_permissions,restrict_log_dir_permissions,restrict_hook_env_to_allowlist,strip_loader_env_in_hooks,disable_powershell_profile_in_hooks,archive_max_extraction_size, anddisable_core_dumps(see "Changed" for its non-default value).
- Concurrency model. The v1 fork-based process manager and thread pool are replaced by a master process that supervises a single worker child (spawned by re-executing the binary) plus an internal 16-way thread pool. The external contract (one master, one worker, PID file, SIGTERM stop) is unchanged.
- Service management on Linux uses a native systemd unit (
Type=simple,Restart=on-abnormal,TimeoutStopSec=7200, control-group kill) instead of the SysV init.d script andsystemd-sysv-generatorcompatibility layer. - The systemd unit launches the agent through
bash -a -c, so lifecycle hooks inherit the full service environment (includingBASH_EXECUTION_STRING), and enablesMemoryAccountingsosystemctl statusreports a Memory line. - Worker respawn uses exponential backoff (5s to 60s, reset after 60s of healthy uptime) instead of a fixed 5s delay, avoiding a tight respawn loop on a persistently crashing worker.
- AWS command client is a purpose-built JSON-1.1/SigV4 implementation instead of the vendored SDK. Retry/backoff lives in the poll loop and the throttle gate; the client itself performs no hidden retries. Endpoint resolution, the exponential backoff formula, the agent-version header, and the 5-minute credential-refresh window all match v1.
- In-place credential refresh. Rotated credentials are picked up without restarting the agent or failing long-running deployments.
disable_core_dumpsdefaults totrue. The agent holds AWS credentials in memory, so core dumps are suppressed (RLIMIT_CORE=0+PR_SET_DUMPABLE=0on Linux,SetErrorModeon Windows). Setdisable_core_dumps: falseto restore the v1 behavior for crash investigation.deploy_control_endpointscheme validation. Onlyhttps://andhttp://are accepted. All other schemes (file:,javascript:,ftp:,gopher:,data:) are rejected at config load. A customhttp://endpoint is accepted (it is a deliberate operator choice, such as a loopback sidecar that terminates TLS) and logs a warning that credentials and command payloads transmit in cleartext.- The local deploy CLI is now the
deploy-localsubcommand of the single binary instead of the separatecodedeploy-localexecutable. All flags are preserved, and the documentedcodedeploy-local [options]invocation keeps working: the.deband.rpminstall acodedeploy-localsymlink alongside the agent binary, which dispatches onargv[0]. - Agent state files and directories stay world-readable by default (files
0644, dirs0755), matching v1, because host tooling outside the agent reads them. Two opt-in flags tighten them:restrict_agent_dir_permissions(install root, per-deployment logs, and state files get dirs0700/0711/0750, log files0640, state files0600) andrestrict_log_dir_permissions(log directory gets dir0750, files0640). On Windows, a protected DACL granting access to SYSTEM and Administrators only is applied to the.versionfile. - Agent self-update downloads from the
latestv2/prefix in the regionalaws-codedeploy-<region>bucket instead oflatest/, which stays frozen on the 1.x installer. Affects both theupdatesubcommand and the service-drivenUpdateDeploymentAgenthost command. Hosts that mirror or allowlist the bucket path must permitlatestv2/install. - The agent configuration ships at
conf/codedeployagent.ymland is installed as the live config (/etc/codedeploy-agent/conf/codedeployagent.yml), replacing thecodedeployagent.example.ymltemplate. The.rpmmarks itnoreplace, so an upgrade preserves local edits. Windows shipsconf/conf.yml. - File writes are atomic (temp file + rename) for crash safety; the observable result is unchanged.
statusreturns LSB-conformant exit codes (3 = not running, 4 = unknown).- The default
wait_between_runsis 30 seconds (matching the v1 code default, not the 1-second fast-poll value in the v1 in-repo sample config).
- AppSpec ACL synthesis emitted two base entries in forms
setfacl --setrejects: the base owner as:<mode>(rejected by strict libacl on Debian and Ubuntu) and the default owner asd:<mode>(rejected on every distribution). Both now use the canonicalu::<mode>/d::<mode>forms, repairing named ACLs, mask inference, and the default-ACL path.
All hardening below was driven by a dedicated threat model and penetration test. Items marked (opt-in) default to v1-compatible behavior and change nothing unless enabled.
- Hook timeout escalation: SIGTERM, then 5s grace, then SIGKILL with a bounded reap. A hook that traps SIGTERM can no longer hang the agent indefinitely. First-signal behavior and the timeout exit code (3) match v1.
scripts.logis size-rotated (64 MiB x 8 files) instead of appended without bound.- ANSI escape sequences are stripped from
scripts.logand the diagnostics shipped to CodeDeploy. - Process-group kill guard: the agent refuses to signal PID 0 (its own group) or a PID greater than
i32::MAX. - Full SELinux label applied:
restorecon -vF(not-v) so the AppSpecuser:andrange:fields actually reach the file label. kill_agent_max_wait_time_secondsis clamped to 24 hours, bounding a misconfiguration while preserving the 2-hour default.- Permission glob matching uses a linear-time matcher (
globset) instead of the hand-rolled matcher, preventing denial-of-service on adversarial AppSpec patterns (CWE-1333). - Hook script location is confined to the deployment archive, and path-traversal containment checks fail closed on an unresolvable path.
- Secrets are redacted from credential debug output, and GitHub tokens are redacted from logs.
- The master process runs with umask
0027(Unix) so agent files created without an explicit mode are not world-readable. The worker resets its umask to0022before extracting customer bundles, so archive entries with no stored Unix mode land at0644and stay readable by the service user. - Created parent directories are
chmoded to their exact intended mode. - (opt-in)
reject_symlinks_in_bundle— reject bundles containing symlinks or hardlinks. - (opt-in)
reject_path_traversal_in_bundle— reject../absolute archive entries (pre- and post-extraction) and..-escaping AppSpecsource,destination, andhooks.locationfields. - (opt-in)
reject_unsafe_permissions_in_bundle— reject SUID/SGID files in bundles and SUID/SGID AppSpec modes. - (opt-in)
reject_unconfined_selinux_in_bundle— reject SELinux types that disable mandatory access control (unconfined_t/kernel_t/init_t). - (opt-in)
archive_max_extraction_size— cap the declared uncompressed bundle size (archive-bomb guard; trusts archive headers). Also rejects truncated or malformed archives during header inspection. - (opt-in)
reject_symlink_permission_targets— reject a symlinked destination in an AppSpecpermissions:block and apply chmod/chown/setfacl/SELinux context with no-follow, closing a TOCTOU privilege-escalation window (CWE-59 / CWE-367). Off by default because bundles that deliberately target a symlink deployed successfully on earlier versions. The write-side symlink race is closed unconditionally regardless of this flag. - (opt-in)
ignore_ownership_in_bundle— own files extracted from tar/tgz bundles as the extracting process instead of applying the archive header's uid/gid. Zip bundles are root-owned either way. - (opt-in)
restrict_agent_dir_permissions— root-only modes for the agent install root, per-deployment logs, and agent state files (see "Changed"). - (opt-in)
restrict_log_dir_permissions— root+group-only modes for the agent log directory. Non-root log collectors (CloudWatch agent, fluentd, Datadog) lose access to the agent and updater logs when this is set. - (opt-in)
restrict_hook_env_to_allowlist— restrict lifecycle hooks to the documented deployment variables (LIFECYCLE_EVENT,DEPLOYMENT_ID,APPLICATION_NAME,DEPLOYMENT_GROUP_NAME,DEPLOYMENT_GROUP_ID,BUNDLE_*) plus a minimal shell environment (PATH/HOME/USER), instead of inheriting the full process environment. - (opt-in)
strip_loader_env_in_hooks— stripLD_PRELOAD/LD_LIBRARY_PATH/LD_AUDITfrom the hook environment (Unix), including values supplied through an AppSpecenvironment:block. - (opt-in)
disable_powershell_profile_in_hooks— run.ps1hooks with-NoProfile -NonInteractive(Windows), preventing profile-script injection and interactive hangs in service context.
- The v1 process-manager configuration keys (
children,wait_between_spawning_children,shared_dir,user,instance_service_*,codedeploy_test_profile) are no longer recognized. They are ignored when present in an upgraded config, and each is logged once so operators can see it had no effect. - Daemon-level privilege dropping (
user/groupself-demotion). Use a systemdUser=override, or AppSpecrunas:for hooks. - The flock-based PID lock file (single-instance is now enforced by the service manager and PID liveness).
- The dynamic plugin system. CodeDeploy is the only plugin and is statically dispatched.
- The
ssl_verify_peerconfiguration key. TLS verification is always on and cannot be disabled. - On-premises registration is not part of the agent binary. Continue to use the existing external registration tooling. The agent reads the resulting on-premises config.
- Existing configuration files load unchanged, including symbol-style keys (
:key: value) in both the agent config and the on-premises credentials file. Unknown and retired keys are ignored rather than fatal, and are logged once. - On-disk layout, instruction/cleanup/tracking file names, deployment-archive structure, ETag verification, deployment-spec PKCS7 signature verification, and AppSpec semantics (versions, file/permission/ACL/SELinux/mode handling, hook ordering and rollback mapping, error codes) all match agent v1.
- Local deployment IDs use the form
local-<pid>instead of v1'sd-XXXXXXXXX-local. Scripts keying on the deployment-root folder name for local deploys are affected.