How to plan Open edX S3 object storage after the MinIO plugin
If you run Open edX with Tutor and MinIO for course media and uploads, start planning your move to S3-compatible object storage now. The Tutor MinIO plugin is deprecated, and its removal is listed for Willow, the next named release, which is listed for December 2026. Before you set a date, confirm the plugin's status in the Tutor release notes for the exact Tutor version you plan to deploy, because that release note is the authority on what is still bundled. The supported replacement is standard Django STORAGES configuration pointed at an S3-compatible endpoint. This section covers the decisions that come before that change, and the sections that follow cover the build and the operating details.
The edx-val settings are the first thing to inventory. Custom storage settings such as VIDEO_IMAGE_SETTINGS and VIDEO_TRANSCRIPTS_SETTINGS are deprecated in Verawood, the current named release published on 30 July 2026, in favor of the standard Django STORAGES configuration. They will be removed in a future release. If your instance sets these values, you are already on a deprecated path. Search your settings files, Tutor plugin configuration and environment overrides for both names, and list every storage backend that serves course media, uploads, video images and transcripts. Each one needs a target in the new configuration, and a gap in that list is the most common reason a storage cutover fails late.
Next, make the design decisions in writing. Decide the endpoint and whether the bucket lives in your cloud account, your data center or an on-premises object store you operate. Choose between one bucket and several, and define a separate credential for each service so that a leaked key has a narrow blast radius. Decide whether learner-facing media is served through public URLs or signed URLs, and confirm how browser uploads will be allowed through CORS and TLS settings. Ask your storage team or vendor to confirm which S3 API operations the endpoint supports, because compatibility gaps are easy to miss until upload or playback fails. Record these choices so that the staging and production environments are built the same way.
Plan data movement and verification as separate work items. Run a full copy to staging first, then compare object counts, sizes and checksums between MinIO and the new store. Test uploads, video playback, transcript downloads and image rendering from a learner account and from a Studio account. Plan for existing course content that still references MinIO hostnames, and for cached URLs in browsers and CDNs. Keep the MinIO data and its backups until the new store has passed a full verification cycle and a rollback has been rehearsed. Ask any implementation partner for a fixed-scope written plan that names the Tutor release, the migration steps, the test cases and the handover items before work starts.
What you need before changing Open edX storage
Storage changes touch the LMS, Studio and the video pipeline at the same time, so the prerequisites come first. The cards below cover the release and Tutor versions, the endpoint and credentials, transport security, a staging copy, the people and documentation needed, and the inventory of today's configuration.
Confirm the Verawood release in use
Open edX supports only the latest named release, and Verawood is the current one, published on 30 July 2026. Confirm which release your LMS and Studio run today. If you are on an earlier release, plan the upgrade to Verawood as a separate step so storage changes do not mix with release changes.
Match the Tutor version to Verawood
Pick the Tutor release that targets Verawood and pin it in your deployment. Check that every Tutor plugin you rely on, including any storage plugin, declares compatibility with that Tutor version. Record the exact Tutor and plugin versions so the staging and production builds match.
A running S3-compatible endpoint
Identify the object storage service you will use, such as Ceph with RGW, MinIO or a cloud bucket service, and confirm it is running in infrastructure you control. Test basic put, get, list and delete operations from the LMS and Studio hosts before any settings change, so storage faults are not confused with application faults.
Buckets and scoped credentials
Create separate buckets for video images, transcripts and other media, and give each its own access key with only the permissions that application needs. Store the keys in your secrets manager rather than in plain configuration files. Record who owns each key and how it will be rotated.
TLS on the storage endpoint
Serve the S3 endpoint over TLS with a certificate issued by a CA your hosts trust, and confirm the LMS and Studio containers validate it. Check the certificate chain from inside the container network, since host-level tests can pass while application containers fail. Decide in advance how certificates are renewed.
A staging copy of the LMS and Studio
Build a staging environment with the same Tutor version, plugins and configuration as production, and restore a recent copy of the database and media into it. Use staging to test uploads, video images, transcript delivery and rollback. Do not test the storage change for the first time against production learners.
Staff who can read the release notes
Assign someone who can read the Verawood developer and operator release notes and the Tutor plugin documentation in full. The storage deprecation is documented there, and the reader must be able to map each deprecated setting to its replacement in your own configuration.
A full inventory of current storage configuration
Before any change, list every storage-related setting in your LMS and Studio configuration, including custom edx-val settings for video images and transcripts, plugin options and environment variables. Note which values point to which bucket or path, and which code paths read them. This inventory is the baseline for the migration and for rollback.
What changes for you when edx-val storage moves to Django STORAGES
The Open edX Verawood release deprecates the custom edx-val storage settings, VIDEO_IMAGE_SETTINGS and VIDEO_TRANSCRIPTS_SETTINGS, in favor of the standard Django STORAGES configuration. The custom settings will be removed in a future release, and the release notes do not give a date for that removal. If your deployment still relies on the custom settings, the practical effect is that you need to move that configuration to STORAGES before the old keys disappear. Treat this as a planned change to schedule now, not a warning to file away.
The settings in scope control how edx-val writes course video images and video transcripts, and similar media written through edx-val. Learners do not see a different experience when the storage backend changes. Video playback, thumbnails and transcript display should continue to work as before, because the URLs that the platform serves are produced by the same application code. Your job is to confirm that after the change, the files still land in the bucket you expect and that the links generated for them still resolve from a learner's browser.
The configuration has to be reviewed in both the LMS and Studio settings. Studio writes course content, including uploaded video assets and transcripts, while the LMS reads and serves much of the same material to learners. If only one service points at the new storage configuration, Studio may upload files that the LMS cannot find, or the reverse. Check that both services use the same bucket names, endpoints and credentials, and that any settings overrides in your Tutor configuration are applied to both sides rather than only one.
Before you change anything, inventory where your deployment currently sets the custom edx-val values and which environment variables or override files feed them. Then decide who owns the change, who tests it in a non-production environment, and how you will confirm the result. A useful check is to upload a test video and transcript through Studio, confirm the objects appear in the storage backend, and then load the course in the LMS as a learner. Record the outcome so the same test can be repeated after your next Verawood upgrade.
Ask your Tutor plugin maintainers or your implementation partner to confirm the exact STORAGES keys that your installed version expects for edx-val, because the setting names for the replacement configuration should be verified against the Verawood developer and operator release notes and your own code, not inferred from the deprecated names. Keep a rollback path that restores the previous configuration in case the new backend behaves differently in production.
Configure S3 object storage for Open edX in this order
This sequence moves Open edX media and edx-val file storage from the MinIO plugin to S3-compatible object storage configured through Django STORAGES. Each step closes with a check, so the team knows the platform can write, serve and deliver files before the next step starts. The result is a tested configuration that the production switch can repeat.
Inventory current MinIO and edx-val settings
The platform team lists every storage-related setting in the LMS and Studio configuration, including the edx-val entries VIDEO_IMAGE_SETTINGS and VIDEO_TRANSCRIPTS_SETTINGS, the MinIO plugin values in the Tutor configuration, and any custom settings files. Record which backend serves course images, video assets, transcripts and uploaded files. Verawood deprecates the edx-val settings in favor of standard Django STORAGES, so flag every custom entry that needs replacing. Done when the team has a written settings map that a second engineer has reviewed.
Confirm endpoint and bucket layout
Confirm with the storage operator the S3 endpoint, region value, TLS requirements and the path or virtual-host addressing style your S3-compatible service expects. Design the bucket layout so course images, video, transcripts and other uploads are separated by bucket or prefix, and decide how public and private objects are handled. Confirm versioning and lifecycle rules with the storage owner, since these depend on your service. Done when a bucket listing from the platform host succeeds with an administrative test identity and the layout is documented.
Define scoped credentials with least privilege
Create a dedicated service identity for each environment, limited to the buckets and actions the LMS and Studio need, such as reading, writing and deleting objects under their designated prefixes. Do not share MinIO root credentials or administrative keys with the application. Store the secrets in your secrets manager and name the team that owns rotation. Done when a bucket listing, a test put and a test get succeed with the scoped credential, and a request outside its prefixes is denied.
Move media settings to Django STORAGES in staging
In a staging environment built from the same Tutor configuration, replace the edx-val storage settings and the MinIO-specific media settings with the Django STORAGES configuration for your S3-compatible backend. Supply credentials through environment variables or secret values only. Restart the services, then check the settings the running LMS and Studio report. Done when a test upload through Studio and a rendered course image both resolve to the new buckets.
Migrate existing objects and references
Copy existing objects from the MinIO buckets to the new buckets with a tool that preserves object keys and checksums, then compare object counts and sizes per prefix. Update stored references if your records hold absolute URLs or bucket names, including course image and transcript references. Run a full copy in staging before production, and keep the MinIO source read-only during the final sync. Done when counts, sizes and sampled checksums match for each prefix and reference checks pass in staging.
Switch production in a planned window
Agree the change window with course operations, because learners may be mid-course and uploads will pause during the switch. Freeze new uploads to the MinIO source, run the final delta copy, deploy the Django STORAGES configuration to production and restart LMS and Studio in the agreed order. Keep the MinIO data in place and the rollback configuration ready until verification passes. Done when production reports the new backend and the change record lists the rollback steps.
Verify uploads, playback and transcript delivery
Upload a test image, a test video and a test transcript file through Studio, then confirm that the course image renders in the LMS, that the video plays for a learner test account, and that the transcript fetch returns the expected file. List each bucket with the scoped credential to confirm the objects landed under the correct prefixes. Watch error logs for storage failures through the first live course activity. Done when every check passes in production and the monitoring owner signs off.
Map old MinIO and edx-val settings to S3 and STORAGES
This table maps each current MinIO and edx-val setting to its target on S3-compatible storage and Django STORAGES, with the staging check and the owning team for each value. Read it as a migration checklist: work down each row, confirm the target in staging, and record the owner before cutover. Items marked as needing confirmation depend on your Tutor version and storage vendor, so verify them against the release you run.
| Current setting or path | Target configuration | What to check in staging | Owner |
|---|---|---|---|
| MinIO endpoint | S3-compatible endpoint URL and region set in the platform settings used by the LMS and Studio | Both services reach the new endpoint, TLS validates, and path or virtual-host addressing matches what the storage service accepts | Platform team |
| MinIO root or access keys | Separate scoped service credentials per service, with read and write limited to the buckets that service uses | Credentials cannot list or write outside their assigned buckets, and no root or admin keys remain in settings or the Tutor configuration | Security |
| edx-val VIDEO_IMAGE_SETTINGS | Equivalent Django STORAGES entry for the video image storage backend, with its location and base URL | Uploaded course video thumbnails are written to the new bucket and render in the LMS and Studio after upload | Platform team |
| edx-val VIDEO_TRANSCRIPTS_SETTINGS | Equivalent Django STORAGES entry for the transcript storage backend, with its location and base URL | Uploaded transcripts are stored, retrieved in the player, and remain available after a service restart | Platform team |
| Bucket names and key prefixes | Named buckets per content type, with fixed prefixes that match the paths already stored in the database | Existing object keys resolve after copy, with no duplicate or orphaned prefixes left behind | Storage team |
| Public read behavior | Public access limited to the objects that learners must load directly, set by bucket policy rather than by application code | Anonymous requests succeed only for the intended prefixes and fail for private paths | Security |
| Signed URL behavior | Signed URL lifetime and signing key set per backend, with expiry values confirmed against the learner experience | Expired links fail as expected, valid links play video and download files, and key rotation is tested | Security |
| Legacy MinIO data | Objects copied to the target bucket with checksums verified, and old paths kept readable until cutover is signed off | Object counts and checksums match per bucket, and sampled files open correctly from both old and new paths | Storage team |
| Tutor plugin configuration | Tutor MinIO plugin removed from the configuration, with storage values supplied through the settings your Tutor release supports | Rendered settings contain no MinIO references, and a fresh deployment starts with the new configuration only | Platform team |
Securing the storage path for learner media
Learner media and transcripts pass through several controls between the platform and the storage backend. Each card below covers one control that the team configures, tests, or hands to the client's security owner before media moves to a new S3-compatible store.
Scope and rotate storage credentials
Give the platform one service identity per environment, with only the read, write and list actions each service needs. Store access keys in the platform's secret handling, never in source-controlled Tutor configuration or values files, and confirm who can read them at runtime. Ask the client to define a rotation schedule and an owner for emergency revocation before go-live. Check that a rotation does not force a platform restart that nobody has planned.
Separate staging and production buckets
Create distinct buckets, credentials and endpoints for staging and production so that test uploads and sample transcripts never land in live learner storage. Confirm that a copied settings file or shared credential cannot point staging at production buckets. Keep bucket names, regions and lifecycle rules in environment-specific configuration. Run a restore test in staging so the process is proven before anyone depends on it in production.
Require TLS to the storage endpoint
Configure the platform to connect to the object store only over HTTPS, and verify the certificate chain against the organization's trusted authority, including for internal endpoints. Check that any custom endpoint URL in the Django STORAGES settings uses an https scheme and that no development override falls back to plain HTTP. Confirm who owns certificate renewal, because an expired certificate stops media delivery for every learner at once.
Deny public listing on buckets
Write bucket policies that deny anonymous listing and deny public access by default, then test them from outside the platform network. Listing is the first risk to test: a bucket that exposes object keys can reveal learner file names even when the contents stay protected. Confirm that the platform's service identity is the only principal with list rights. Review the policy whenever a new media type or storage path is added.
Control access to uploads and transcripts
Learner uploads and transcripts need a documented decision on whether object URLs are public or signed. Public URLs are simpler to serve but work for anyone holding the link, while signed URLs expire and depend on the platform's session checks. The client's security owner should make this decision in writing, and the team should test that an expired signed URL fails and that transcript keys cannot be guessed from sequential names.
Encrypt at rest and log access
Confirm that the storage backend encrypts objects at rest, and document where the encryption keys are held and who can rotate them. Enable access logging or audit logging on each bucket, send the logs to a store the security team can search, and set retention to match the client's policy. Check that each log entry records the caller identity, so a single learner's file access can be traced when an incident review asks for it.
Common failures when moving Open edX media to S3
This table lists the failures teams most often hit when moving Open edX media off the MinIO plugin and onto S3, with the likely cause, a check to run and the fix for each. Read it during cutover planning and again during test runs, since most rows can be verified with one request or a settings search. The main takeaway is that uploads, existing media, transcripts, credentials, TLS and caching each fail separately, so test each one before switching traffic.
| Failure | Likely cause | Check to run | Fix |
|---|---|---|---|
| Uploads succeed in Studio but do not render in the LMS | Studio and the LMS resolve different storage endpoints or public base URLs | Compare the asset URL in LMS page source with the bucket URL Studio writes to | Set one public base URL for both services, then redeploy Studio and the LMS |
| Existing media returns 404 after cutover | Object keys or prefixes were not copied to the new bucket | List keys under the old and new prefixes and compare counts for a sample course | Copy the full prefix set with checksum verification, then run a delta copy before cutover |
| Transcripts missing after cutover | VIDEO_TRANSCRIPTS_SETTINGS or related settings still point at MinIO | Search all settings files for the MinIO endpoint and the old bucket name | Move transcript storage to Django STORAGES as described in the Verawood operator notes |
| Credentials list objects but cannot write | The policy grants bucket listing but lacks object write and delete actions on the media prefix | Run a put, get and delete test with the service credential on a test key under the media prefix | Scope the policy to the bucket and prefix Studio and the LMS use, then retest |
| Certificate errors when connecting to the endpoint | The endpoint certificate comes from a private CA that the Open edX containers do not trust | Open a TLS connection to the endpoint from inside the LMS and Studio containers | Add the CA bundle to the container trust store and confirm the storage client reads it |
| Stale CDN or browser caches after the move | Cached responses still point to old URLs or old objects | Request a sample asset with a cache-busting query and compare its headers with the origin | Purge CDN paths for media, update cache rules, and ask course teams to hard refresh |
Questions buyers ask about Open edX S3 storage work
What drives the cost of moving Open edX media to S3 storage?
The main cost drivers are the number of media objects to copy and verify, whether custom storage code is needed, the endpoint type, how many environments must change, and how much validation the cutover requires. A small catalog on the standard configuration is a different job from a large archive with custom storage classes across several environments. The engagement is one-time, but the storage bill for the endpoint recurs and is paid directly to your storage provider. Ask for a quote that prices each driver separately so you can see which choices move the total. DeployOpen plans most engagements as fixed scope, with deliverables and timelines agreed in writing before work starts.
How long does an S3 storage change for Open edX take?
No single duration applies to every installation, so treat any number as provisional until the scope is written. Duration follows the same drivers as cost: object count, custom code, number of environments and depth of validation. One environment on the standard S3 settings with a staging copy moves faster than a multi-environment rollout that includes a legacy media archive and several sign-off rounds. Confirm the schedule in the written scope, including staging validation, the cutover window and the rollback test. Ask which tasks sit on the critical path, such as copying media or waiting for credentials from your storage team.
What must our team supply before Open edX storage work begins?
Your team supplies the storage endpoint, credentials with access limited to the buckets the platform needs, and a staging copy of the LMS and Studio data that includes media. You also name an approver who signs off the cutover, and you provide access to the Tutor configuration and deployment environment. Someone who can explain existing media paths and retention rules should be available during discovery. Delays usually come from credentials that take time to issue or from a staging copy that is out of date. Record each dependency in the scope so the schedule shows who owns each one.
Can our internal team change Open edX storage settings alone?
Yes, if the team has working Django and Tutor experience and a tested rollback. The change touches Django STORAGES configuration, Tutor settings and the media that already sits in the old storage, so an engineer who has only run Tutor in a test setup will need more time. Test the rollback on staging before the production cutover. Read the Verawood developer and operator release notes on the deprecation of VIDEO_IMAGE_SETTINGS and VIDEO_TRANSCRIPTS_SETTINGS, and confirm which release your site runs before you begin. An external partner makes sense when the team lacks one of these skills or when the cutover window is tight.
Why must we move Open edX media off MinIO before Willow?
The Tutor MinIO plugin is deprecated and removed in Willow, which the Open edX release notes list for December 2026. Verawood, the current named release published on 30 July 2026, deprecates the custom edx-val settings VIDEO_IMAGE_SETTINGS and VIDEO_TRANSCRIPTS_SETTINGS in favor of the standard Django STORAGES configuration. Those settings will be removed in a future release. Open edX supports only the latest named release, so sites on an older release also face upgrade work. Plan the storage move as part of your release path, and confirm removal dates in the release notes before you set an internal deadline.
What should an Open edX storage quote itemize?
A quote should list each piece of work and who performs it, so you can compare it with an internal plan. Ask for these items as separate lines:
- Discovery: inventory of media types, object counts and existing storage paths
- Configuration: Tutor and Django STORAGES changes for each environment
- Custom code: any storage class or adapter, with its tests
- Data copy: transfer method, verification method and retry handling
- Staging validation: test cases for learner-facing pages and video or transcript playback
- Cutover and rollback: the rollback test and the sign-off steps
- Documentation and handover: runbook and operator notes
Also ask which items are excluded, such as storage subscription fees, and which items recur after handover.
What usually breaks after Open edX media moves to S3?
The most common failures are broken media URLs from hardcoded paths or old hostnames, object permissions that block public or private content, and CORS rules that stop image, video or transcript requests from the LMS domain. Signed URLs with short expiry can fail for learners on long sessions, and copies that skip prefixes or object versions leave gaps. Caching layers can also keep serving old paths after the cutover. Check each risk with a sample of learner-facing pages on staging, and compare object counts between source and target. Confirm endpoint-specific behavior with your storage provider's documentation before the cutover.
How DeployOpen scopes and delivers Open edX storage changes
DeployOpen plans and implements Open edX object storage changes inside your own cloud accounts, data centers or air-gapped networks. Work starts with a written scope that ties the storage design to the Open edX release you run, since Verawood deprecates the custom edx-val storage settings in favor of Django STORAGES. Engagements are fixed-scope, with deliverables and timelines agreed in writing before work begins.
How we scope
- The written scope lists every storage backend, bucket and Open edX service that reads or writes media.
- The scope records your Open edX release, Tutor version and whether the MinIO plugin needs replacing before Willow.
- Ongoing support for the storage layer is available as a retainer, scoped separately from the fixed build.
How we build
- Map every edx-val setting in VIDEO_IMAGE_SETTINGS and VIDEO_TRANSCRIPTS_SETTINGS to a Django STORAGES entry.
- Configure S3-compatible endpoints, credentials and bucket policies inside your infrastructure, then test uploads, reads and transcript delivery.
- Replace the MinIO plugin's configuration in Tutor settings and plugin files, with a rollback path kept for the cutover.
How we hand over
- A written record of each storage setting, its Django STORAGES mapping and the Open edX release it applies to.
- Test evidence from upload, playback and transcript checks, kept with the configuration for your change records.
- A runbook covering credential rotation, bucket backups and the steps to follow at the next Open edX upgrade.
How we support
- Upgrade planning for each named release, because Open edX supports only the latest named release.
- Post-change review of storage access logs and error rates with your operations team, scoped in the retainer.
- Confirm with your storage vendor and Tutor version that any remaining MinIO references are removed before Willow lands.

