Updating the image of a service named like "api.test" wrote a nested
"test" key into the "api" service, because data_get/data_set split the
name on dots. Use array key paths instead. Also refuse service names that
Docker cannot use as a container name prefix, with a clear error on save.
Closes#12456
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cloning a clone added another "-clone-<id>" to the name, so after about
seven clones the name was longer than 255 characters and the insert failed.
Closes#9976
The compose parser queued ServerFilesFromServerJob for every volume on
each parse, and pages that only show data parse the Compose file. Each
sync also sent two SSH commands for each file.
- Queue the sync only for a compose deployment, and for each service
resource with file storages when the service starts or deploys.
- Read the compose service names without a parse on the Domains and
scheduled task pages.
- Check all file storages with one remote command, and stop when a
symbolic link, a special file, or an unknown state is at a file path.
- Keep the application path in a file storage when a pull request
preview is parsed. Preview deployments of the current parser write
their `-pr-<id>` copies through LocalFileVolume::forPullRequest().
- Remove the unreachable file sync from the service resource page.
The UI service clone iterated relation builders before parse() created
the sub-resources, so no volume copy ever ran. All service clone paths
now use clone_service(), which parses first and matches volumes by
mount path. VolumeCloneJob no longer forces chown 1000:1000.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts between main's security fixes and v4.5 features:
- Keep v4.5 features (log redaction, multi-S3 backups, transfer flow,
scheduled task modal, sidebar) and port main's fixes into them.
- Reuse RequiresDeployAbility for the Hostinger create endpoint.
- Align MCP tool abilities and descriptions with main's new deploy
requirements, and use deploy tokens in the affected MCP tests.
- Drop the duplicate DockerCleanupJobTest helper and tests, and the
exportBundle test for a method v4.5 removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Require deploy permission for API changes that trigger server side effects, including resource deletion, storage access, provider operations, and database imports. Also escape Docker cleanup filters and sanitize log drain server names.
When a compose service copies another service's SERVICE_URL_/SERVICE_FQDN_
declaration (old Twenty template: worker repeats SERVICE_URL_TWENTY_3000),
the parser and updateCompose wrote the variable from the copier's generated
domain, so a custom domain on the named service was lost on every deploy.
Skip exact copies; declarations with another path or port are unchanged.
Closes#11924
Sentry self-hosted (getsentry/self-hosted 26.8.0) could not be deployed
as a Docker Compose application. Three separate problems blocked it:
- YAML collection aliases: the first fix raised the limit to 256 only in
Application::oldRawParser(). The safety check added in v4.4.0
(validateDockerComposeForInjection) and all other Compose parse calls
still used the Symfony default of 128, so the Sentry file (133 aliases)
failed with "Maximum number of collection aliases (128) exceeded".
Add parseDockerComposeYaml() and use it for every Compose parse.
- Argument list too long: the deployment job sent the generated Compose
file as base64 inside the SSH command. Linux limits one argument to
128 KiB, and the Sentry file is about 370 KB. execute_remote_command()
now accepts an 'input' key that streams content over SSH stdin, and
the job writes the Compose file with it.
- A list-style build arg without a value (build.args: [SENTRY_IMAGE])
merged into a map environment kept its numeric key, so Docker Compose
rejected the file with "non-string key in ...environment: 0". It now
becomes NAME with no value, as in Compose.
Closes#11725
Since aabd12737 a Compose bind volume with a content block had to stay
inside the resource directory. Services and Compose applications saved
in v4.3.23 with other paths, and Git Compose files with them, no longer
started or deployed.
Only admins and owners can edit Compose files, and they can already
write any host file with a plain bind mount, so the rule added no
security.
- Write content files to their host path again, as in v4.3.23
- Keep shell escaping and the safe-character checks on volume sources,
so injection attempts are still rejected
A non-root SSH user cannot open redirects, expand globs, or cd into
/data/coolify (9999:root 0700). Several remote commands relied on that
and failed on non-root servers.
- Write README files with `tee` instead of shell redirects in database
starts and application deployments
- Clear Postgres init scripts with `find` instead of a glob
- Split the proxy config backfill if/else so each branch gets sudo
- Start the log drain with `docker compose -f` instead of `cd`
- Wrap the preserved-repository compose start in `sh -c` for non-root
- Stage clone archives outside /data/coolify via StagesCloneArchives
- Extend the sudo parser and add non-root command tests
- Compose content files are written by a queued job. Deployments and
service starts now check all content files with one command before
docker compose up: a missing file or an empty directory (left by
Docker) is written; an existing file is kept; a directory with files
is kept with a visible warning.
- The server file sync that runs after every parse marked a file
storage as a directory and deleted its configured content when a
directory was at its path. A storage with content now keeps its
content; an empty directory is replaced with the file.
- Saving a storage no longer fails for an existing file, replaces an
empty directory, and no longer deletes a directory with files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A Compose bind volume with a Coolify `content:` block could make
Coolify write any host file (for example /root/.ssh/authorized_keys)
when the file was loaded. Content sources must now be inside the
resource directory (./ paths); other sources get a clear validation
error for services and applications (UI, API, load, deployment),
and every content write is confined again on the server. Bind
mounts without content are unchanged. All 94 template content
volumes use ./ paths and still work.
- Quote container names in scheduled tasks and in the stop actions for
applications, previews, and services.
- Fix a development-only crash when a preview with a bind mount was
parsed (the preview suffix returned a plain string).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Truncate GetLogs against raw byte length, then strip ANSI colors.
Run scheduled-task docker exec with explicit sudo docker and no_sudo
to avoid double sudo rewriting on non-root servers. Mark git-based
file volumes as files before refreshing content from the server.
Stop catting remote file contents into memory for non-git bind mounts in
getFilesystemVolumesFromServer; git-based volumes still load via
loadStorageOnServer().
This commit introduces advanced environment variable handling capabilities including:
- Nested environment variable resolution with circular dependency detection
- Extraction of hardcoded environment variables from docker-compose.yml
- New ShowHardcoded Livewire component for displaying detected variables
- Enhanced UI for better environment variable management
The changes improve the user experience by automatically detecting and displaying
environment variables that are hardcoded in docker-compose files, allowing users
to override them if needed. The nested variable resolution ensures complex variable
dependencies are properly handled.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed from `->before('-')` to `->beforeLast('-')` to correctly parse service
names with hyphens. This fixes prerequisite application for ~230+ services
containing hyphens in their template names (e.g., docker-registry,
elasticsearch-with-kibana).
Added comprehensive test coverage for hyphenated service names and fixed
existing tests to use realistic CUID2 UUID format. All unit tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactors the Appwrite and Beszel service-specific application settings
to use a centralized constant-based approach, following the same pattern
as NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK.
Changes:
- Added NEEDS_TO_DISABLE_GZIP constant for services requiring gzip disabled
- Added NEEDS_TO_DISABLE_STRIPPREFIX constant for services requiring stripprefix disabled
- Created applyServiceApplicationPrerequisites() helper function in bootstrap/helpers/services.php
- Updated all service creation flows to use the centralized helper:
* app/Livewire/Project/Resource/Create.php (web handler)
* app/Http/Controllers/Api/ServicesController.php (API handler - BUG FIX)
* app/Livewire/Project/New/DockerCompose.php (custom compose handler)
* app/Http/Controllers/Api/ApplicationsController.php (API custom compose handler)
- Added comprehensive unit tests for the new helper function
Benefits:
- Single source of truth for service prerequisites
- DRY - eliminates code duplication between web and API handlers
- Fixes bug where API-created services didn't get prerequisites applied
- Easy to extend for future services (just edit the constant)
- More maintainable and testable
Related commits: 3a94f1ea1 (Beszel), 02b18c86e (Appwrite)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The updateCompose() function now correctly detects SERVICE_URL_* and
SERVICE_FQDN_* variables regardless of whether they are defined in
YAML list-style or map-style format.
Previously, the code only worked with list-style environment definitions:
```yaml
environment:
- SERVICE_URL_APP_3000
```
Now it also handles map-style definitions:
```yaml
environment:
SERVICE_URL_TRIGGER_3000: ""
SERVICE_FQDN_DB: localhost
```
The fix distinguishes between the two formats by checking if the array
key is numeric (list-style) or a string (map-style), then extracts the
variable name from the appropriate location.
Added 5 comprehensive unit tests covering:
- Map-style environment format detection
- Multiple map-style variables
- References vs declarations in map-style
- Abbreviated service names with map-style
- Verification of dual-format handling
This fixes variable detection for service templates like trigger.yaml,
langfuse.yaml, and paymenter.yaml that use map-style format.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Parse template variables directly instead of generating from container names. Always create both SERVICE_URL and SERVICE_FQDN pairs together. Properly separate scheme handling (URL has scheme, FQDN doesn't). Add comprehensive test coverage.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>