whoami
Lucas Jenß
cat /etc/motd
The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.
ls -la ~/languages/
- drwxr-xr-x
- ▶ PHP
- ▶ Ruby
- ▶ Scala
- ▶ C#
- ▶ JavaScript
- ▶ Objective-C
- ▶ Shell Scripting
ls -la ~/toolchain/
- drwxr-xr-x
- ▶ Typo3
- ▶ Akka
- ▶ Capistrano
- ▶ Git
- ▶ MAMP
- ▶ Adobe Illustrator
- ▶ NSTrackingArea (Cocoa)
uname -a
- drwxr-xr-x
- ▶ Mac OS X
- ▶ Unix
Run TYPO3 commands in cron with the right environment
A TYPO3 command can work perfectly in an SSH session and fail when moved to cron. The usual cause is not cron itself but the environment it creates: a minimal PATH, a different working directory, no interactive shell profile, and sometimes a different HOME or timezone. TYPO3 also needs the Composer installation and application configuration before its console kernel can bootstrap.
This matters to Australian teams hosting TYPO3 in Sydney or Melbourne, where deployments may target a managed VPS, a cPanel or Plesk server, or a container platform. A deployment user can have the right files while the cron daemon still starts with an unsuitable PHP binary. The command name is only the final layer; project root, PHP extensions, dotenv values, database credentials, cache paths and TYPO3 context all have to line up.
The dependable approach is to put the command in a small wrapper, run that wrapper as the deployment user, and add only a short cron entry. Testing must reproduce cron’s limited environment rather than rely on the developer’s shell. This pattern applies to scheduler:run, cache:warmup, a database update or a custom maintenance command while keeping console invocation separate from scheduling policy.
Why cron changes the TYPO3 runtime
Cron normally changes directory to the account’s home unless the entry changes it. It also receives a reduced environment and does not load .bashrc or .profile. A Composer project is therefore easy to break in two ways: vendor/bin/typo3 may not be found, or TYPO3 may bootstrap from the wrong root and look for missing configuration. A migrated host can add another variable: VMware vMotion CPU failures describes how an infrastructure move exposed an incompatibility hidden on the original host, a useful reminder to compare PHP builds and loaded extensions after infrastructure changes.
The runtime differences are predictable:
| Runtime detail | Interactive terminal | Cron entry |
|---|---|---|
| Working directory | Usually the project root | Often the user’s home |
| PHP executable | Resolved through the shell PATH |
May resolve differently |
| Environment | Shell exports and profiles | Daemon defaults only |
| Home and temporary paths | Login user’s values | Account-dependent values |
| Timezone | User and PHP settings | Daemon and PHP settings may differ |
Change to the Composer project root before invoking vendor/bin/typo3; otherwise the autoloader and configuration lookup can fail. Use an absolute PHP path such as /usr/bin/php8.3, because cron’s PATH may differ from an SSH login. The same command can then behave consistently on a local workstation, a CI worker and an Australian production host.
Time settings need explicit attention as well. A server in Sydney moves between AEDT and AEST, while a Brisbane host stays on AEST. The scheduler evaluates due work against the effective application time, so an incorrect PHP timezone can make jobs appear early, late or not due. Record the server’s zone with timedatectl, check PHP’s configured timezone, and decide whether business schedules should follow Sydney local time, Brisbane local time or UTC before enabling the job.
Build a command wrapper for the deployment
Create a wrapper beside the deployment scripts and make it executable. Keep environment defaults explicit, but leave secrets to TYPO3’s environment-file or secret-provider mechanism. This example runs any TYPO3 console command passed to it:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 027
APP_ROOT="/srv/typo3/example-site"
PHP_BIN="/usr/bin/php8.3"
export HOME="${HOME:-/home/deploy}"
export APP_ENV="${APP_ENV:-prod}"
export TYPO3_CONTEXT="${TYPO3_CONTEXT:-Production}"
cd "$APP_ROOT"
exec "$PHP_BIN" vendor/bin/typo3 "$@"
Replace the paths with the values on the target host, and use command -v php to identify the installed binary. Australian Ubuntu servers may provide several PHP versions, so assuming php8.3 can select the wrong runtime. The wrapper’s working directory lets Composer and TYPO3 find vendor/ and the application configuration, while exec preserves the command’s exit status instead of hiding it behind another shell.
Run the wrapper as the Unix user that owns the release. Using root may create root-owned cache files that the web server cannot replace later. A common Australian hosting custom is to keep production under a deploy account while panel users create panel-level cron jobs; map the panel job to this wrapper and grant write access only to the required log and lock directories. Test it with a deliberately limited environment:
sudo -u deploy env -i HOME=/home/deploy PATH=/usr/bin:/bin \
/srv/typo3/example-site/bin/run-typo3 list
Use the hosting provider’s user-switch command if sudo is unavailable. Run list first to confirm that TYPO3 loads and discovers the expected commands, then execute the real command. A successful cache:warmup can also reveal whether cache directories are writable, while scheduler:run will expose database, site configuration and scheduler problems.
Confirm environment values and filesystem paths
TYPO3 loads environment-specific settings through its supported dotenv and configuration mechanisms, but precedence matters. A value exported by the wrapper can override a repository .env file, which is useful for APP_ENV and TYPO3_CONTEXT but risky for secrets. Keep APP_SECRET and database credentials in deployment-restricted storage and ensure the selected context can read them. Never paste secret values into a crontab because crontabs and process arguments may be readable by other accounts.
For a command supplied by a custom extension, first prove that its service is registered and its configuration is available. The implementation in a custom scheduler task demonstrates the task and configuration path for a cleanup job. The same registration requirements apply when that task is triggered through the CLI. A wrapper cannot compensate for a missing extension, an invalid site identifier or stale Composer autoload files.
Confirm these runtime values without printing their secret contents:
APP_ENV=prodselects the Symfony runtime profile used by the release.TYPO3_CONTEXT=Productionenables production TYPO3 behaviour rather than development tooling.APP_SECRETmust be available to features requiring signed or encrypted state.TYPO3_DB_*values must identify the same database as the web application when the deployment uses them.
Then confirm the filesystem paths:
vendor/bin/typo3andvendor/autoload.phpmust belong to the installed release.- The
config/directory and site-package configuration must be readable. var/cache/andvar/log/must be writable by the deployment user.- Dotenv files selected for the release must exist and protect any sensitive values.
Schedule the job with the right clock
Once the wrapper succeeds manually, schedule the wrapper rather than Composer or a long inline shell command:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME=/home/deploy
*/5 * * * * /srv/typo3/example-site/bin/run-typo3 scheduler:run >> /srv/typo3/example-site/var/log/typo3-cron.log 2>&1
Cron normally sends command output to local mail, so explicit redirection keeps failures in a predictable location. Protect and rotate that log because it may contain file paths, identifiers or exception details. The crontab contains no application secrets; the wrapper retrieves them through the deployment’s protected configuration mechanism.
A long-running command can also overlap with its next scheduled execution. On Linux, flock provides a simple guard:
*/5 * * * * flock -n /srv/typo3/example-site/var/lock/typo3.lock /srv/typo3/example-site/bin/run-typo3 scheduler:run >> /srv/typo3/example-site/var/log/typo3-cron.log 2>&1
Create the lock directory with suitable ownership before enabling this entry. If the host does not provide flock, add an equivalent lock inside the wrapper or use TYPO3’s own scheduler protection.
Cron interprets schedules in the daemon’s timezone, which need not match PHP or the business location. Adelaide changes between ACST and ACDT and sits half an hour behind Sydney during daylight saving, while Perth remains on AWST without daylight saving. Avoid assuming that every cron implementation handles a skipped or repeated wall-clock time identically. Check date, timedatectl and PHP’s timezone, and make the daemon and application agree before relying on an exact daily schedule.
Deploy, verify and make failures visible
Deploy in a controlled order: install Composer dependencies as the release owner, warm the cache, test the wrapper as the deployment user, and only then enable cron. A cron entry should never run composer install or an extension setup command concurrently with live traffic because an interrupted installation can leave the application inconsistent. Keep the wrapper in version control while storing site-specific values and secrets outside the repository.
When a job fails, compare the wrapper, PHP runtime and TYPO3 configuration separately. Run the command with increased console verbosity, inspect the cron log and confirm that the process exit status is non-zero. This explicit failure signalling follows the same useful principle described in actor failure detection strategies: monitoring works when the failed component reports a clear signal rather than leaving the operator to infer success from silence. Send cron failures to the existing alerting system and test that alert before production use.
Create and commit the wrapper with the deployment repository, execute it once as the deployment user, confirm the expected command and exit code, and then enable the locked cron entry. Keep secrets out of the crontab, rotate the job log and verify the monitoring path before declaring the schedule operational.
cat ~/interests.json
| Key | Value |
|---|---|
| editor | Terminal-first workflow |
| os | Mac OS X / Unix |
| vcs | Git, distributed version control |
| deploy | Capistrano, cron automation |
| graphics | SVG, Adobe Illustrator troubleshooting |
| networking | IP validation, SSH, VPN |
git log --oneline --reverse
Solving SVG import issues in Adobe Illustrator CS6 and CC
When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.
Solving NDK build issues on OS X
Troubleshooting native development kit compilation problems on Mac OS X.
Programmatically adding PHP generated TypoScript to the backend configuration
Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.
ArgumentError: Could not parse PKey: no start line
Debugging an SSH key parsing error encountered during deployment.
Validating IP-Addresses in PHP
Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.
Cocoa: Using NSTrackingArea
A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.
cat ~/contact.txt