Plugin Development for Craft Cloud
As long as your plugin’s design and implementation follows our established best practices, it should work on Craft Cloud without changes. All Craft features are available on Cloud, making the platform’s architectural differences inconsequential when using official, documented APIs.
Plugins must be compatible with at least Craft 4.5.10 (the minimum version of Craft required to run on Cloud), but they may support earlier versions.
Want to test your plugin on Cloud? Get in touch for a free sandbox environment!
Special Considerations #
There are still a few things about Craft Cloud that may require reevaluation of some assumptions about how web applications are run. The Cloud module and Craft itself provide everything you need to make your plugin reliable and resilient across the widest possible variety of hosting platforms.
Ephemeral Filesystem #
Craft Cloud sets the CRAFT_EPHEMERAL
config override, which tells Craft to treat file writes as unreliable (or downright impossible). Plugins should honor this setting by checking craft\helpers\App::isEphemeral()
before trying to perform any writes to the disk.
If you must write files to disk, use the /tmp
directory. Do not hard-code this path, or construct it dynamically. Instead, use Craft’s Path
service to get information about system directories’ locations:
$path = Craft::$app->getPath();
$tmp = $path->getTempPath();
$storage = $path->getStoragePath();
$cache = $path->getCachePath();
// ...
Logs
Writing logs directly to a file is not recommended. Always use the Logger
component (Craft::$app->getLogger()
) or the static Craft::info()
, Craft::warning()
, and Craft::error()
convenience methods to ensure logs are sent to Cloud’s aggregated log target.
File Responses #
In situations where a controller needs to return a file, it should instead upload the artifact to S3 and redirect to a pre-signed URL. For control panel requests, the Cloud module handles this automatically; $this->sendFile($path)
and any other methods that set a response’s stream
property will work normally.
The web runtime is implicitly authorized to interact with its environment’s storage bucket—see the Cloud module for an example of how we get a URL to an uploaded object.
Asset Filesystems #
While we recommend that projects on Cloud use our first-party Cloud filesystem, it is not a requirement. Filesystem types provided by plugins may need to implement a custom uploader to send binary files directly to the storage provider (rather than POST them directly to the Craft application).
The Cloud filesystem’s client-side and server-side code is available for reference.
Other File Uploads #
In situations where you need a file from a user (like a plugin that works with CSVs), consider providing an asset selection input, instead:
{% import forms from '_includes/forms' %}
{{ forms.elementSelect({
name: 'file',
elementType: 'craft\\elements\\Asset',
limit: 1,
}) }}
Doing so will take advantage of the existing volumes and filesystems, while still providing access to the underlying file’s content via craft\elements\Asset::getStream()
.
Other Best Practices #
Plugins that already embrace our existing coding guidelines and best practices should be Cloud-ready—or take significantly less time to make compatible.
In addition, these tips can help avoid hiccups when your plugins are deployed to Craft Cloud:
- Asset Bundles’
sourcePath
must begin with your plugin’s predefined alias.- Assets must be registered for use in the front-end via
craft\web\View::registerAssetBundle()
. Publishing one-off assets is not supported—if you want an asset to be available at runtime, it must be encapsulated in a bundle. - Bundle classes must be instantiable even if Craft is not fully installed, or cannot connect to a database. Plugin settings and other system state may be unavailable to the build environment when we are publishing assets.
- Your plugin’s main
composer.json
should always have atype
ofcraft-plugin
, and any downstream packages containing asset bundles (i.e. utilities shared by multiple plugins) must have atype
beginning withcraft
oryii
to be picked up by our publishing routine.
- Assets must be registered for use in the front-end via
- Implement batched jobs if your workload is apt to exceed 15 minutes. It is often better to spawn many small jobs than a single long-running one.
- Don’t set cookies unless absolutely necessary—like in response to a user action.
- If possible, register JavaScript to fetch CSRF tokens asynchronously. Using
{{ csrfInput() }}
in a front-end template will immediately set a cookie and prevent caching.
- If possible, register JavaScript to fetch CSRF tokens asynchronously. Using
Publishing Your Plugin #
Cloud-compatible plugins go through the same submission and approval process as regular plugins. Once you’ve verified your plugin works on Cloud, be sure and check the Tested on Craft Cloud in its management screen on Craft Console!
Getting Help #
Please reach out to us if you have questions about your plugin’s compatibility.