Using DigitalOcean Spaces or Amazon S3 for file storage in XF 2.1+

Using DigitalOcean Spaces or Amazon S3 for file storage in XF 2.1+

No permission to download
Compatible XF 2.x versions
  1. 2.1
  2. 2.2
XenForo Version Compatibility

The download is only compatible with XenForo 2.1 and above.

Why this guide?

Since XenForo 2.0.0 we have supported remote file storage using an abstracted file system named Flysystem. It's called an abstracted file system as it adds a layer of abstraction between the code and a file system. It means that it provides a consistent API for performing file system operations so that whether the file system is a local disk-based file system or a distributed and remotely accessible file system, our code calls the same functions and Flysystem takes care of the rest.

As useful as that is, it isn't the most obvious or straightforward thing to set up so this guide and accompanying add-on will help.

So, if you're planning to make use of the video uploads function in XF 2.1 or above and you're worried about increased disk space requirements this will help.


Making the required files available

Although it is possible for you to download the files and set up things like the autoloader yourself, you will probably prefer to simply download the add-on that is attached to this resource. You can install the add-on in the usual fashion.


Before you start

If you're setting this up on an existing site, you will need to manually move your existing files over. There's a section about that at the end. While you are moving the existing files, setting things up and testing, we recommend closing the forum first.


Setting up DigitalOcean Spaces

We'll cover this first as it is the most straightforward to set up. If you'd prefer to use Amazon S3 skip ahead to the Setting up Amazon S3 section below.
  1. Go to the DigitalOcean Cloud page and sign up or log in.
  2. At this point, if you're new to DigitalOcean, you may need to set up billing.
  3. You will now be able to create a new project.
  4. Click the "Start using Spaces" link.
  5. Choose your datacenter region (I have chosen Amsterdam).
  6. Leave "Restrict File Listing" selected.
  7. Choose a unique name (I have chosen "xftest")
  8. Click "Create a space"
Now the space is created, you should have an endpoint URL, similar to: https://xftest.ams3.digitaloceanspaces.com. Note this down for later.

Now we need to create some API credentials. To do this:
  1. Click "Manage" in the left sidebar.
  2. Click "API".
  3. In the "Spaces access keys" section click "Generate New Key".
  4. Type a name for the key (Again, I have chosen "xftest") and save.
This will give you a key and a secret. Note them down.

Configuring XF to use DigitalOcean Spaces

We now need to configure XF to use DigitalOcean Spaces for file storage. We'll start with what usually goes into the data directory first. This generally includes attachment thumbnails and avatars.

Open your src/config.php file.

First we need to configure the Amazon S3 client (the DigitalOcean Spaces API is compatible with the Amazon AWS SDK).

We will do this using a closure so that we can reuse the same code and we only have to type it out once:
PHP:
$s3 = function()
{
   return new \Aws\S3\S3Client([
      'credentials' => [
         'key' => 'ABC',
         'secret' => '123'
      ],
      'region' => 'ams3',
      'version' => 'latest',
      'endpoint' => 'https://ams3.digitaloceanspaces.com'
   ]);
};

Note that the key and secret are what you noted down after setting up the "Spaces access key" earlier. The region can be inferred from the endpoint URL you noted down earlier. It's the part after the first . in the URL, in my case it is ams3. The endpoint is the same endpoint URL minus the unique name you chose.

Next we need to set up the actual Flysystem adapter to use the S3 client:
PHP:
$config['fsAdapters']['data'] = function() use($s3)
{
   return new \League\Flysystem\AwsS3v3\AwsS3Adapter($s3(), 'xftest', 'data');
};

Finally, we need to ensure that avatar and attachment thumbnail URLs are prepended with the correct URL. This requires the endpoint URL you noted down earlier, again:
PHP:
$config['externalDataUrl'] = function($externalPath, $canonical)
{
   return 'https://xftest.ams3.digitaloceanspaces.com/data/' . $externalPath;
};

At this point, everything should be working in terms of new uploads. Don't be alarmed if you notice that avatars and thumbnails are missing; if you have existing files, they will need to be moved over manually which we'll go through later.

First, we need to test that the configuration works. Simply go and upload a new avatar. The avatar will now be stored and served remotely!

If you check your DigitalOcean Spaces account now, you should see that new folders have been created containing your new avatar:

187981


Success! 🌟 But we're only half way there!

We now need to add support for the internal_data directory stuff too. Generally, this is attachments and any other stuff that should be "private". Back to config.php and the code to add is very similar:
PHP:
$config['fsAdapters']['internal-data'] = function() use($s3)
{
   return new \League\Flysystem\AwsS3v3\AwsS3Adapter($s3(), 'xftest', 'internal_data');
};

Now try to upload an attachment to a post and, much like before, you should now see additional files and folders in your Spaces file browser. 🎉


Setting up Amazon S3
  1. Go to the AWS Management Console page and sign up or log in.
  2. In the "AWS services" section type "S3" to go to the "S3 Console".
  3. Click "Create bucket".
  4. Choose a bucket name (I have chosen xftest).
  5. Choose a region (I have chosen EU London).
  6. Accept any further default options until the bucket is created.
  7. You now need to go to the "IAM" console.
  8. Click "Add user".
  9. Pick a username (yep, I used xftest again 😉).
  10. Set the access type to "Programmatic".
  11. To set permissions, click the "Attach existing policies directly" tab followed by the "Create policy" button.
  12. IAM and the various policies and permissions can be fairly daunting. We can make it a bit easier, though you may have different requirements. On this page there is a tab called "JSON". Paste the following in there, replacing YOUR-BUCKET-NAME with the bucket name you chose earlier:
JSON:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:putObject",
                "s3:putObjectAcl",
                "s3:ReplicateObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR-BUCKET-NAME",
                "arn:aws:s3:::YOUR-BUCKET-NAME/*"
            ]
        }
    ]
}
  1. Click "Review policy" give it a name and save.
  2. Go back to the previous "Add user" page, click the "Refresh" button and search for the policy you just created.
  3. Click "Next", followed by "Create user".
This will give you a key and a secret. Note them down.


Configuring XF to use Amazon S3

We now need to configure XF to use Amazon S3 for file storage. We'll start with what usually goes into the data directory first. This generally includes attachment thumbnails and avatars.

Open your src/config.php file.

We will do this using a closure so that we can reuse the same code and we only have to type it out once:
PHP:
$s3 = function()
{
   return new \Aws\S3\S3Client([
      'credentials' => [
         'key' => 'ABC',
         'secret' => '123'
      ],
      'region' => 'eu-west-2',
      'version' => 'latest',
      'endpoint' => 'https://s3.eu-west-2.amazonaws.com'
   ]);
};

Note that the key and secret are what you noted down after setting up the IAM user earlier. The region can be inferred from the S3 endpoint URL.

Next we need to set up the actual Flysystem adapter to use the S3 client:
PHP:
$config['fsAdapters']['data'] = function() use($s3)
{
   return new \League\Flysystem\AwsS3v3\AwsS3Adapter($s3(), 'xftest', 'data');
};

Finally, we need to ensure that avatar and attachment thumbnail URLs are prepended with the correct URL:
PHP:
$config['externalDataUrl'] = function($externalPath, $canonical)
{
   return 'https://xftest.s3.eu-west-2.amazonaws.com/data/' . $externalPath;
};

At this point, everything should be working in terms of new uploads. Don't be alarmed if you notice that avatars and thumbnails are missing; if you have existing files, they will need to be moved over manually which we'll go through later.

First, we need to test that the configuration works. Simply go and upload a new avatar. The avatar will now be stored and served remotely!

If you check your bucket file browser now, you should see that new folders have been created containing your new avatar:

187995


Success! 🌟 But we're only half way there!

We now need to add support for the internal_data directory stuff too. Generally, this is attachments and any other stuff that should be "private". Back to config.php and the code to add is very similar:
PHP:
$config['fsAdapters']['internal-data'] = function() use($s3)
{
   return new \League\Flysystem\AwsS3v3\AwsS3Adapter($s3(), 'xftest', 'internal_data');
};

Now try to upload an attachment to a post and, much like before, you should now see additional files and folders in your bucket file browser. 🎉


Moving existing files to DigitalOcean Spaces or Amazon S3

So, you now have remotely hosted files. At least, you do from this point onwards. But what about all of the existing files you have?

Thankfully there are several ways to interact with Spaces and S3 in order to make moving your existing content over very easily. Although this is a one-time operation, depending on the number and size of the files, it could take a significant amount of time.

There are a few ways to manage this process, but arguably the best approach is to use a tool by the name of s3cmd which is a popular cross-platform command-line tool for managing S3 and S3-compatible object stores.

It should be possible whether you are using Spaces or S3 to install the s3cmd tool on your server and run the commands to copy the files across to their new home.

Rather than rehashing something that has already been written, I'll leave you with the following guide from DigitalOcean which goes through how to migrate your existing files using s3cmd.

Note: When copying your existing data files across, they will need to be made public. You can do this by setting the ACL to public while copying:
Author
XenForo
Downloads
484
Views
25,112
First release
Last update

Ratings

5.00 star(s) 7 ratings

More resources from XenForo

Latest updates

  1. Minor change: Smaller add-on download

    This update is a very minor maintenance release. As well as including the latest version of the...
  2. Updates Amazon AWS SDK to version 3.209.16

    The last update to this add-on updated the Amazon AWS SDK to version 3.204.2 but it implied this...
  3. Update for PHP 8.1 compatibility

    There are no changes here apart from added support for PHP 8.1. It's up to you whether you...

Latest reviews

Great info here but I have a few things to add.

Would advise using S5cmd over S3cmd. It is 100x faster. Does not support sync (don't worry, they have an alternative command/method for this, read the docs) but the initial upload takes a lot less time. This is important if you are running an install with 100k plus users on it, each user generates 5-6 files so over half a million 1-2KB files. You will want S5cmd for this!

If you are using Cloudflare, make use of their features. Even more so if you are using BackBlaze, no bandwidth charges!
Use a CNAME to proxy your S3 bucket.
https://help.backblaze.com/hc/en-us/articles/217666928-Using-Backblaze-B2-with-the-Cloudflare-CDN

As your URL will include the bucket ID, and the bucket is public, hide it and make the URL look pretty with Cloudflare workers! https://jross.me/free-personal-image-hosting-with-backblaze-b2-and-cloudflare-workers/

AFAIK BackBlaze B2 does not have access control, unlike most other S3 providers. With access control, you can have a public bucket, but whitelist Cloudflare IP addresses so that no one can crawl your public bucket. Randomize your bucket names anyway! No need to share the bucket name with anyone when you proxy the traffic...
If you use Xenforo to proxy the traffic, you can set your bucket to private anyway.
Hopefully, Xenforo devs can come up with a solution to have public and private attachments delivered separately so one can use S3/proxy to deliver all public attachments while proxying only private attachments via the forum. Just saves on requests hitting the forum server more than anything but also a big save on bandwidth if you are hosting many images and video.
hands down a great resource for any admins to use to offload media. Easy setup and quick. Would highly recommend!
Nice and fast, works great. Was able to install and get it running in less than 10 minutes. Note, easy to install ZipArchive on ubuntu 20:

sudo apt install php-zip
Easy to setup, works great. This addon is a real lifesaver when you find outself running out of server storage space.
I love this add-on, works as expected
But there is a note you should know, if you want to disable this add-on, you should remove the code in config file before click disable add-on
It's been almost a year I don't see add-on update, I hope Chris D could update it (if it needed to improve)
It works, easily configurable, and good documentation ! Thanks for your great work and the time development !
It took a little bit to configure properly, but once I did it works great. Thank you for taking the time to create this plugin!!
Top Bottom