Follower Nodes
What this page covers
This page only explains how to connect another AsterDrive instance as a follower node, and how the primary node registers the remote node, generates an enroll command, and verifies connectivity.
If your primary instance is not running yet, start with Deployment Overview.
If the follower uses Docker
Docker followers can now read bootstrap ENV at container startup and enroll automatically. If you do not want to enter the container manually to run aster_drive node enroll, go directly to Docker Follower Deployment.
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 ingress target pushed by the primary
The current internal remote storage protocol version is v2. When the primary tests connectivity and binds remote policies, it reads capability information exposed by the follower, including protocol version range, server version, object read/write capabilities, Range capabilities, compose capabilities, metadata capabilities, and the CORS contract required for browser direct upload.
By default, AsterDrive runs in primary mode. It becomes a follower node only after [server].start_mode is changed to follower.
This is not a multi-primary cluster
A follower node is not a second login site or a second admin console.
It has only one goal: provide a remote object storage target for the primary node. If you need multi-primary hot standby, automatic failover, or cross-region strong-consistency replication, the current capability does not cover those scenarios.
Enrollment Flow
Primary node is already available
|
+-- Set the public site URL
|
+-- Admin -> Follower Nodes: create node record
| |
| +-- Generate one-time enroll command
|
+-- Follower node: run node enroll
| |
| +-- Write follower configuration and binding information
|
+-- Restart follower service
|
+-- Test connectivity from the primary admin console
|
+-- Create default ingress target
|
+-- Create remote storage policy
|
+-- Put the policy into a policy group and assign it to users or teamsThe easiest step to miss
Successful enroll does not mean uploads are ready. Before the follower can really handle remote storage, you still need to create a default ingress target for it from the primary node.
Confirm These Before Enrollment
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
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.
base_url Decides Whether the Primary Can Actively Reach the Follower
When creating a remote node record, base_url can be left empty temporarily. This lets you register and enroll first, but the primary node later has two limits:
- It cannot actively test connectivity
- It cannot really send remote storage traffic to the follower
So the conclusion is simple:
- Only registering first:
base_urlcan be empty - If it needs to serve a remote storage policy: you must fill an
http://orhttps://address reachable by the primary
Current boundary for followers behind NAT
The current version has no reverse-connection channel. If the follower can actively access the primary but the primary cannot actively access the follower, enroll may complete, but remote storage capability probing, ingress target synchronization, and object reads/writes will not work normally.
This direction is tracked in issue #136. For now, if you want to use a follower as a remote storage policy, the primary still needs to reach the follower's base_url.
Use a Local Ingress Target First
In the current version, where a follower receives objects is created by the primary node in Admin -> Follower Nodes. The name is ingress target. For the first follower, create a local ingress target first and use a simple relative path, for example:
defaultThis path is restricted by the follower under its own server.follower.managed_ingress_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
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 namespace to assign to this follower
The namespace does not need to be complex. For a first test, split by environment, region, or tenant, for example:
home-storagehangzhou-atenant-a
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 ingress targets,
[server.follower].managed_ingress_local_rootpoints to a directory with enough capacity
The most direct approach is editing config.toml:
[server]
start_mode = "follower"
[server.follower]
managed_ingress_local_root = "data/managed-ingress"If you deploy with Docker, you can also override with environment variables:
ASTER__SERVER__START_MODE=follower
ASTER__SERVER__FOLLOWER__MANAGED_INGRESS_LOCAL_ROOT=/data/managed-ingress
ASTER__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
Entry:
Admin -> Follower NodesThe three most important fields when creating the record:
- Name: human-readable, for recognizing it in the admin console and policies
- Namespace: the object prefix agreed by primary and follower
base_url: the address the primary will use to access the follower
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
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 primary binding and receive namespace into the follower locally
- 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 an ingress target. Ingress targets are now 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
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"
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 must be compatible with the current primary before you continue creating a remote storage policy. The current primary requires followers to support at least internal protocol v2.
6. Create an Ingress Target on the Primary
Return to:
Admin -> Follower NodesOpen the follower you just connected and find primary-managed ingress targets. This decides where objects written by the primary to the follower finally land.
Two ingress 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 ingress target"
The local path here can only be relative and is always restricted under the follower's:
[server.follower]
managed_ingress_local_root = "data/managed-ingress"That means base_path = "default" ultimately lands under a directory such as data/managed-ingress/default on the follower. If you want the follower to write objects directly to S3, create an s3 ingress target here and fill endpoint, bucket, credentials, and optional prefix.
Remote writes are rejected without a default ingress target
Successful enroll only means the primary-follower identity binding succeeded. Before actually receiving objects, the follower still needs an applied default ingress target. Otherwise, remote policy uploads return "no default ingress target yet".
Ingress targets are pushed by the primary through the follower API, so there are two more prerequisites:
- The remote node must have a
base_urlreachable by the primary - The current follower can only bind to one primary; multiple primary bindings reject this managed ingress target mode
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 can only bind to a remote node that is enabled and has
base_urlconfigured - Where the follower actually writes is decided by the default ingress target from the previous step
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
Remote policies check more than whether base_url is reachable. 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
If you choose remote presigned, the browser accesses the follower directly, so the follower's reverse proxy must also pass 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
Can base_url Be Empty and Still enroll?
Yes.
But the result is only:
- The primary saves the node record
- The follower writes the primary binding
It still cannot:
- Let the primary actively test connectivity
- Really write remote storage traffic to this node
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 an Ingress 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?
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?
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.