Resources

How to copy objects between Managed Object Storage instances on a schedule

Published on: October 14, 2026

If your backup or disaster recovery plan, or a compliance requirement, calls for a copy of your objects in a second region, you can keep one up to date with a scheduled copy. Managed Object Storage doesn't support S3 bucket replication, see S3 standard compatible. This guide describes how to set up a Cloud Server that copies new and changed objects from a bucket in one Managed Object Storage instance to a bucket in another, every five minutes. The copy runs over private connections and keeps the destination bucket equal to the source bucket, deletions included. After each successful run, it writes a marker object that records when the run started, so you can see how recent the copy is.

Turn on versioning on both buckets, as described below, so that overwritten and deleted objects stay available as earlier versions.

Before you start

You need:

  • Two Managed Object Storage instances, for example one in europe-2 and one in europe-1. The source instance has the bucket to copy. The destination instance has a bucket for the copy and a separate bucket for the marker.
  • A user for each instance. The source user needs s3:GetObject, s3:ListBucket and s3:GetBucketLocation on the source bucket. The destination user needs full access to the destination bucket and the marker bucket. To turn on versioning on the source bucket and to copy objects back, you also need a source instance user with full access to the source bucket. You can create users and policies in the Control Panel, on the instance's Users and Policies tabs, see Getting started with Managed Object Storage, or with the API, see How to create a user for an Object Storage instance using the API and How to apply user access policies using the API.
  • An SDN Private Network with a router attached, in the location where the copy runs, for example de-fra1, and a Cloud Server on it. Object Storage instances can only be attached to a network that has a router. Run the commands in this guide as root on that server.

Attach the instances to the network before you create the server, so that the server gets the routes to the instances' private endpoints when it starts. If the server already exists, restart it after attaching the instances.

This guide covers the following operating systems, installed from the UpCloud templates:

  • Ubuntu 22.04, 24.04, and 26.04
  • Debian 12 and 13
  • AlmaLinux 8, 9, and 10
  • Rocky Linux 8, 9, and 10
  • Fedora 42 and 43

Attach both instances to the network

Attach each Object Storage instance to the server's private network. Both instances can be attached to the same network, even when they're in different regions. The attachment takes a few minutes. Wait until the instance is running again before you create or restart the server.

  1. In the Control Panel, open the Object Storage instance. On its Overview page, under Private access, click Attach private network.

  2. Select the Network, then enter a Name for the attachment.

    Attach private network window
  3. Click Attach. The instance's status shows Setting up network… until the attachment is ready, and then Running.

  4. Repeat for the other instance.

When the attachment is ready, Private access shows the instance's private endpoints and the attached network.

Private access section with an attached network

Install rclone

Install the current rclone release from the rclone project, and create the folder for its configuration file:

apt-get update && apt-get install -y curl
curl -fsSLo /tmp/rclone.deb https://downloads.rclone.org/rclone-current-linux-amd64.deb
apt-get install -y /tmp/rclone.deb
mkdir -p /root/.config/rclone

Configure rclone

Save the following as /root/.config/rclone/rclone.conf, with each user's access keys and each instance's private endpoint. You can find the private endpoint in the Control Panel, on the instance's Overview page under Private access (the S3 endpoint), or in the instance's details from the API.

[src]
type = s3
provider = Other
access_key_id = <source-access-key>
secret_access_key = <source-secret-key>
endpoint = https://<source-private-endpoint>
region = <source-region>

[dst]
type = s3
provider = Other
access_key_id = <destination-access-key>
secret_access_key = <destination-secret-key>
endpoint = https://<destination-private-endpoint>
region = <destination-region>

Check that the server reaches both buckets. Each command lists the bucket's contents, or prints nothing for an empty bucket, without an error:

rclone lsf src:<source-bucket>
rclone lsf dst:<destination-bucket>

Turn on versioning

Turn on versioning on the destination bucket:

rclone backend versioning dst:<destination-bucket> Enabled

The source user can only read the source bucket, so turn on versioning on the source bucket with a user that has full access to it. See How to enable and manage S3 object versioning.

To remove earlier versions after a set number of days, see How to configure lifecycle policies.

Schedule the copy

  1. Save the copy script as /usr/local/bin/copy-objects:

    #!/bin/bash
    set -euo pipefail
    start=$(date -u +%FT%TZ)
    rclone sync src:<source-bucket> dst:<destination-bucket> --transfers 16 --checkers 16 --s3-no-check-bucket
    echo "$start" | rclone rcat dst:<status-bucket>/last-copy
  2. Save the service as /etc/systemd/system/copy-objects.service:

    [Unit]
    Description=Copy objects to the other Object Storage instance
    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/copy-objects
  3. Save the timer as /etc/systemd/system/copy-objects.timer:

    [Unit]
    Description=Run the object copy every 5 minutes
    [Timer]
    OnBootSec=2min
    OnUnitActiveSec=5min
    [Install]
    WantedBy=timers.target
  4. Make the script executable and start the timer:

    chmod 755 /usr/local/bin/copy-objects
    systemctl daemon-reload
    systemctl enable --now copy-objects.timer

Check the copy

The marker object holds the start time of the last successful run, and the timer shows when the next run starts:

rclone cat dst:<status-bucket>/last-copy
systemctl list-timers copy-objects.timer

If the marker is older than two intervals, the copy has stopped. To see why, run journalctl -u copy-objects.service.

Copy in the other direction

The copy runs in one direction only: each run makes the destination bucket match the source bucket. If you switch your application to the destination bucket, a later run deletes the objects it adds there and replaces the objects it changes with the source's copies. Before you switch, turn off the timer:

systemctl disable --now copy-objects.timer

To switch your application back to the source bucket, first copy the objects back from the destination bucket. The copy back makes the source bucket match the destination bucket. Objects that are only in the source bucket are deleted, and with versioning on they stay available as earlier versions.

The source user only reads the source bucket, so add a remote for the source instance user with full access to the source bucket. Add it to the end of /root/.config/rclone/rclone.conf:

[back]
type = s3
provider = Other
access_key_id = <source-writer-access-key>
secret_access_key = <source-writer-secret-key>
endpoint = https://<source-private-endpoint>
region = <source-region>

Copy back while your application isn't writing to the destination bucket, so that the source bucket gets all of its changes:

rclone sync dst:<destination-bucket> back:<source-bucket> --transfers 16 --checkers 16 --s3-no-check-bucket

Then switch your application to the source bucket, and turn the timer back on to copy in the original direction again:

systemctl enable --now copy-objects.timer
Contributed by: Samir Haliru

Can't find what you're looking for?

For more help you can contact our awesome 24/7 support team