Follower Nodes
Concepts First
Section titled “Concepts First”AsterDrive’s remote-node capability essentially lets another AsterDrive instance act as a storage backend.
- Primary node: handles login, frontend, admin console, shares, WebDAV, storage policies, and remote-node management
- Follower node: only provides
/health,/health/ready, and the internal remote storage protocol; it accepts object requests signed by the primary node, then writes objects to a follower local directory or S3 according to the remote storage target pushed by the primary
The current internal remote storage protocol version is v5, and the current primary supports followers whose compatible range overlaps v4 through v5. When the primary tests connectivity and binds remote policies, it compares the declared version ranges and reads the follower’s server version, object read/write capabilities, Range capabilities, compose capabilities, metadata capabilities, and the CORS contract required for browser direct upload. Followers limited to v2 or v3 must be upgraded first.
By default, AsterDrive runs in primary mode.
It becomes a follower node only after [server].start_mode is changed to follower.
Enrollment Flow
Section titled “Enrollment Flow”Confirm These Before Enrollment
Section titled “Confirm These Before Enrollment”Primary and Follower Must Be Independent
Section titled “Primary and Follower Must Be Independent”They may communicate with each other, but they must not share the same data/, database, upload directory, or temporary directory.
At minimum, these must be independent:
data/config.toml- Database file or external database connection
- Local upload directory
- Temporary directory
public_site_url Is Required Before enroll
Section titled “public_site_url Is Required Before enroll”When the primary node generates an enroll command, it reads directly from:
Admin -> System Settings -> Site Configuration -> Public site URLIf this is not set to a real reachable HTTP(S) origin, the admin console cannot sign the command. With multiple origins, the enroll command uses the first line as the primary address, so place the primary address reachable by the follower on the first line.
Choose Transport Before Deciding base_url
Section titled “Choose Transport Before Deciding base_url”When creating a remote node record, choose one of three transport modes:
| Transport | How to fill base_url | Best for |
|---|---|---|
| Direct | Required; an HTTP(S) follower address reachable by the primary | Same datacenter, same private network, VPN, existing reverse proxy |
| Reverse tunnel | Can stay empty | The follower can reach the primary, but the primary cannot connect back to the follower |
| Auto | Uses direct when base_url is set; uses reverse tunnel when it is empty | Let the presence of an address decide the route |
auto does not fail over to the reverse tunnel after a direct connection fails. It only checks whether base_url is empty.
If a remote policy needs presigned upload or download, use direct transport and make sure browsers can also reach the follower base_url. Reverse tunnel is suitable for relay_stream; it currently cannot generate presigned URLs that browsers use to connect directly to the follower.
If your follower will sit behind public HTTPS, Tailscale / VPN, a Docker network, or reverse tunnel, read Follower Node Network Topologies first. That page explains what base_url means to the primary and to browsers separately.
Use a Local Remote Storage Target First
Section titled “Use a Local Remote Storage Target First”In the current version, the primary creates the follower’s object destination under Admin -> Follower Nodes. The UI calls it a Remote Storage Target. It is the place where the follower finally writes objects, either to a local directory or to S3.
For the first follower, create a local remote storage target first and use a simple relative path, for example:
defaultThis path is restricted by the follower under its own server.follower.remote_storage_target_local_root, so the primary cannot write arbitrary host paths.
The reason is not that “S3 cannot be used”; it is that getting the primary-follower path working first, then switching to a more complex target, lowers diagnosis cost.
1. Configure the Primary Node First
Section titled “1. Configure the Primary Node First”The primary node is a normal primary deployment.
Before connecting a follower, confirm:
- The primary admin console opens normally
Public site URLis set- You have decided the follower name and transport mode
The name does not need to be complex. For a first test, name it by environment, region, or tenant, for example:
home-storagehangzhou-atenant-a
2. Prepare the Follower Instance
Section titled “2. Prepare the Follower Instance”The follower uses the same aster_drive binary as the primary. Only the runtime mode is different.
At minimum, confirm:
- It has its own working directory and data volume
- Its
[server].start_modeisfollower - If you use primary-managed local remote storage targets,
[server.follower].remote_storage_target_local_rootpoints to a directory with enough capacity
The most direct approach is editing config.toml:
[server]start_mode = "follower"
[server.follower]remote_storage_target_local_root = "remote-storage-targets"If you deploy with Docker, you can also override with environment variables:
ASTER__SERVER__START_MODE=followerASTER__SERVER__FOLLOWER__REMOTE_STORAGE_TARGET_LOCAL_ROOT=/data/remote-storage-targetsASTER__DATABASE__URL=sqlite:///data/asterdrive.db?mode=rwcThese ASTER__... environment variables use the same startup configuration as config.toml, but with higher priority.
If you want a Docker follower to enroll automatically during its first startup, you also need an additional one-time bootstrap ENV set. See Docker Follower Deployment for the complete form.
What if the current directory does not have `config.toml` yet?
When there is no configuration file in the current directory, aster_drive node enroll generates a default data/config.toml in follower mode and initializes database state at the same time.
But you must decide at least:
- Whether this directory is the real working directory the service will use later
- Whether the
data/under this directory will be persisted
Avoid completing enroll in a temporary directory, then letting systemd or Docker use another data volume in actual service.
3. Register the Remote Node on the Primary
Section titled “3. Register the Remote Node on the Primary”Entry:
Admin -> Follower NodesThe three most important fields when creating the record:
- Name: human-readable, for recognizing it in the admin console and policies
- Transport mode: direct, reverse tunnel, or auto
base_url: required for direct; optional for reverse tunnel; in auto mode, empty means reverse tunnel and non-empty means direct
After saving, the admin console generates a one-time command, roughly like:
aster_drive node enroll --master-url https://drive.example.com --token enr_xxxxxThis token expires after 30 minutes by default. If it expires, go back to the primary node and generate a new one.
4. Run enroll on the Follower
Section titled “4. Run enroll on the Follower”Enter the follower’s own working directory and run the command generated earlier.
If you need to specify the database explicitly, add the parameter like this:
aster_drive node enroll \ --master-url https://drive.example.com \ --token enr_xxxxx \ --database-url sqlite:///data/asterdrive.db?mode=rwcThis command does several things:
- Exchanges the token with the primary node for one-time bootstrap configuration
- Writes the primary binding locally on the follower; the object isolation prefix is generated automatically by the follower
- Writes the enroll receipt back to the primary node so the primary knows this enrollment has completed
Note that this step does not automatically create a remote storage target. Remote storage targets are pushed from the primary node in remote node details. The reason is simple: administrators need to see, change, and test them in one place later, and it avoids having to reconstruct old CLI parameters on the follower machine.
If the current configuration is still primary mode, the CLI errors directly and asks you to change start_mode to follower first.
This is expected protection to avoid accidentally enrolling a normal primary instance as a follower.
5. Restart the Follower Service, Then Test from the Primary
Section titled “5. Restart the Follower Service, Then Test from the Primary”In the current version, after enroll writes the primary binding into the database, the running follower service does not hot-reload it. So the flow must be:
- Run
node enroll - Restart the follower service
- Return to the primary node and click “Test connection”
The connection test uses the node’s current transport mode: direct nodes access base_url; reverse-tunnel nodes use the outbound channel maintained by the follower. For reverse tunnel, wait a few dozen seconds after restart and test after the tunnel status becomes online.
One easy-to-misread detail:
| Endpoint | Before enroll | After enroll |
|---|---|---|
/health | Returns 200, meaning the process is alive | Should still return 200 |
/health/ready | Returning 503 is normal because there is no active primary binding yet | After restart and successful enrollment, should return 200 |
Before enroll, /health/ready returning 503 does not mean the service is broken.
It is not ready before enrollment by design.
After the connectivity test passes, the primary shows a capability summary in remote node details. At minimum, the protocol version range must be compatible with the current primary before you continue creating a remote storage policy. The current primary uses v5 and supports followers down to v4; v2 / v3 followers must be upgraded first.
6. Create a Remote Storage Target on the Primary
Section titled “6. Create a Remote Storage Target on the Primary”Return to:
Admin -> Follower NodesOpen the follower you just connected and find Remote Storage Targets. This decides where objects written by the primary to the follower finally land.
Two remote storage target types are currently supported:
local: write to the follower’s local directorys3: write to S3 / MinIO / R2 or similar object storage reachable by the follower
For the first attempt, create local:
- Use an easy-to-recognize name, such as
default-local - Use a relative base path, such as
default - Check “Set as default remote storage target”
The local path here can only be relative and is always restricted under the follower’s:
[server.follower]remote_storage_target_local_root = "remote-storage-targets"That means base_path = "default" ultimately lands under a directory such as data/remote-storage-targets/default on the follower.
If you want the follower to write objects directly to S3, create an s3 remote storage target here and fill endpoint, bucket, credentials, and optional prefix.
Remote storage targets are pushed by the primary through the follower API, so there are a few more prerequisites:
- Direct nodes must have a
base_urlreachable by the primary - Reverse tunnel nodes, and
autonodes with emptybase_url, must show the tunnel as online - The current follower can only bind to one primary; multiple primary bindings reject this primary-managed remote storage target mode
7. Create a Remote Storage Policy on the Primary
Section titled “7. Create a Remote Storage Policy on the Primary”After the follower is connected, return to the primary:
Admin -> Storage PoliciesCreate a Follower Node type storage policy there. The complete policy group routing, test user binding, and launch validation steps are in Remote Follower Storage Policy Tutorial.
Its biggest differences from local / S3 policies are:
- The real network transfer, access key, and signature are all handled by the “remote node” record
- The policy itself only controls remote path prefix, upload limits, and whether it is the default
- A remote storage policy should bind to a remote node that is enrolled, enabled, and reachable through its current transport mode
- Where the follower actually writes is decided by the remote storage target bound to the policy; when none is selected explicitly, the default target is used
In other words, remote storage policies no longer configure endpoint, access key, or secret key. That layer is already managed by the remote node record.
After creating the policy, put it into a policy group or set it as the default route. From then on, it behaves like local / S3 routes.
Protocol Capabilities and Extra Requirements for presigned
Section titled “Protocol Capabilities and Extra Requirements for presigned”Remote policies check more than whether the current transport can connect. The primary validates follower capabilities according to the upload/download mode of the current policy:
- Basic reads/writes require object
GET,HEAD,PUT, andDELETE - Folder and object maintenance require
list,compose, andmetadata - Preview, resume, and streaming reads require
range_getandaccept_ranges_header - Remote
presignedupload or download also requiresbrowser_presigned_cors, and the remote node must not be using reverse tunnel
If you choose remote presigned, the browser accesses the follower directly. Use direct transport, make sure browsers can reach the follower base_url, and make sure the follower’s reverse proxy passes the CORS headers of the internal storage API.
Upload presigned requires at least:
- Allowed request header:
content-type - Exposed response header:
ETag
Download presigned requires at least:
- Allowed request header:
range - Exposed response headers:
Accept-Ranges,Content-Range,Content-Length
The browser CORS contract declared by the default follower currently covers content-type, range, and exposes GET-required Accept-Ranges, Cache-Control, Content-Disposition, Content-Length, Content-Range, Content-Type, ETag, plus PUT-required ETag. If you put a reverse proxy in front of the follower, do not swallow these headers.
Common Judgment Questions
Section titled “Common Judgment Questions”Can base_url Be Empty and Still enroll?
Section titled “Can base_url Be Empty and Still enroll?”Yes, but it depends on the transport mode:
direct: you can only save the record and complete enrollment. Withoutbase_url, the primary cannot test connectivity or send remote storage traffic.reverse_tunnel: after the follower restarts, it actively connects to the primary. Once the tunnel is online, you can test connectivity, push remote storage targets, and userelay_streamremote policies.auto: emptybase_urlbehaves like reverse tunnel; a non-emptybase_urlbehaves like direct.
For any mode, remote presigned upload/download requires direct transport and a follower base_url reachable from the browser.
Can the Follower Node Be Opened for Regular User Login?
Section titled “Can the Follower Node Be Opened for Regular User Login?”No. In the current design, follower nodes are not regular user login entries.
follower mode only exposes:
/health/health/ready- Internal remote storage API
Can a Remote Storage Target Use Another remote Policy?
Section titled “Can a Remote Storage Target Use Another remote Policy?”No.
When a follower receives inbound objects, the target must be directly writable on the follower side, such as local or s3; it cannot wrap another remote layer.
Why Restart After enroll Succeeds?
Section titled “Why Restart After enroll Succeeds?”Because the current version only writes the binding into the database and does not hot-refresh the running follower process. Write succeeded does not mean it has taken effect. It starts receiving traffic only after restart.
What Happens When a Remote Node Is Disabled?
Section titled “What Happens When a Remote Node Is Disabled?”Remote policies on the primary stop using it, and the follower also rejects corresponding signed inbound requests. Disabling actually stops the link; it does not merely hide the admin record.