Vortos
Object Store

Object Store Quickstart

Install and configure Vortos Object Store for Cloudflare R2 or another S3-compatible provider.

Object Store Quickstart

Install The Package

composer require vortos/vortos-object-store

Install vortos/vortos-docker as well when you want vortos:worker:* supervisor management commands.

Register The Package

bootstrap/app.php
use Vortos\ObjectStore\DependencyInjection\ObjectStorePackage;

$packages = [
    // ... other packages
    new ObjectStorePackage(),
];

Set Environment Variables

Cloudflare R2:

.env
VORTOS_OBJECT_STORE_DRIVER=s3
OBJECT_STORE_PROVIDER=r2
OBJECT_STORE_REGION=auto
OBJECT_STORE_ACCOUNT_ID=your-cloudflare-account-id
OBJECT_STORE_BUCKET=your-bucket
OBJECT_STORE_ACCESS_KEY_ID=your-r2-access-key
OBJECT_STORE_SECRET_ACCESS_KEY=your-r2-secret-key
OBJECT_STORE_TEMPORARY_KEY_PREFIX=tmp
OBJECT_STORE_PUBLIC_BASE_URL=https://cdn.example.com

AWS S3:

.env
VORTOS_OBJECT_STORE_DRIVER=s3
OBJECT_STORE_PROVIDER=s3
OBJECT_STORE_REGION=us-east-1
OBJECT_STORE_BUCKET=your-bucket
OBJECT_STORE_ACCESS_KEY_ID=your-access-key
OBJECT_STORE_SECRET_ACCESS_KEY=your-secret-key
OBJECT_STORE_TEMPORARY_KEY_PREFIX=tmp

Development:

.env
VORTOS_OBJECT_STORE_DRIVER=log
OBJECT_STORE_BUCKET=dev-bucket

Testing:

.env.test
VORTOS_OBJECT_STORE_DRIVER=null
OBJECT_STORE_BUCKET=test-bucket

Create Config

config/object_store.php
<?php

declare(strict_types=1);

use Vortos\ObjectStore\DependencyInjection\VortosObjectStoreConfig;

return static function (VortosObjectStoreConfig $config): void {
    $config
        ->driver($_ENV['VORTOS_OBJECT_STORE_DRIVER'] ?? 'log')
        ->provider($_ENV['OBJECT_STORE_PROVIDER'] ?? 'r2')
        ->region($_ENV['OBJECT_STORE_REGION'] ?? 'auto')
        ->endpoint($_ENV['OBJECT_STORE_ENDPOINT'] ?? null)
        ->bucket($_ENV['OBJECT_STORE_BUCKET'] ?? '');

    $config->client()
        ->accountId($_ENV['OBJECT_STORE_ACCOUNT_ID'] ?? null)
        ->credentials(
            $_ENV['OBJECT_STORE_ACCESS_KEY_ID'] ?? null,
            $_ENV['OBJECT_STORE_SECRET_ACCESS_KEY'] ?? null,
        );

    $config->bucketConfig()
        ->temporaryKeyPrefix($_ENV['OBJECT_STORE_TEMPORARY_KEY_PREFIX'] ?? 'tmp')
        ->publicBaseUrl($_ENV['OBJECT_STORE_PUBLIC_BASE_URL'] ?? null);
};

Run Migrations

The object-store package ships the object_store_outbox migration. Run your normal project migration command after package registration.

Install The Outbox Worker

php bin/console vortos:worker:install --worker=object-store-outbox-relay

Plan Temporary Upload Cleanup

php bin/console vortos:object-store:lifecycle plan
php bin/console vortos:object-store:lifecycle apply --confirm

Lifecycle commands mutate provider infrastructure

Vortos never applies lifecycle rules during HTTP requests, worker boot, or container compilation. Apply lifecycle changes explicitly in deployment or operations workflows.

Generate A Direct Upload Intent

use Vortos\ObjectStore\Contract\DirectUploadManagerInterface;
use Vortos\ObjectStore\ValueObject\TemporaryUploadUrlOptions;

final class CreateUploadIntentHandler
{
    public function __construct(private readonly DirectUploadManagerInterface $uploads) {}

    public function handle(CreateUploadIntent $command): UploadIntentDto
    {
        $intent = $this->uploads->createUploadIntent(
            temporaryKey: sprintf('tmp/%s/%s', $command->userId, $command->fileName),
            options: TemporaryUploadUrlOptions::forDirectUpload(
                ttlSeconds: 900,
                contentType: $command->contentType,
                maxSizeBytes: $command->maxSizeBytes,
            ),
        );

        return UploadIntentDto::fromIntent($intent);
    }
}

On this page