aboutsummaryrefslogtreecommitdiff
path: root/doc/book/src
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2021-10-19 16:16:10 +0200
committerAlex Auvolat <alex@adnab.me>2021-10-25 14:21:48 +0200
commitde4276202ad2be8a2e07f2a6f2f48d9c25cdc32c (patch)
tree2e156181891c312c6b64122250ff9b5c25c91bef /doc/book/src
parent1b450c4b493dfcb2ee88acbca3ea584beac8eb4b (diff)
downloadgarage-de4276202ad2be8a2e07f2a6f2f48d9c25cdc32c.tar.gz
garage-de4276202ad2be8a2e07f2a6f2f48d9c25cdc32c.zip
Improve CLI, adapt tests, update documentation
Diffstat (limited to 'doc/book/src')
-rw-r--r--doc/book/src/SUMMARY.md1
-rw-r--r--doc/book/src/cookbook/real_world.md174
-rw-r--r--doc/book/src/quick_start/index.md42
-rw-r--r--doc/book/src/reference_manual/configuration.md83
-rw-r--r--doc/book/src/reference_manual/s3_compatibility.md99
-rw-r--r--doc/book/src/working_documents/migration_04.md61
6 files changed, 230 insertions, 230 deletions
diff --git a/doc/book/src/SUMMARY.md b/doc/book/src/SUMMARY.md
index 264714b1..771a2cad 100644
--- a/doc/book/src/SUMMARY.md
+++ b/doc/book/src/SUMMARY.md
@@ -30,3 +30,4 @@
- [Working Documents](./working_documents/index.md)
- [Load Balancing Data](./working_documents/load_balancing.md)
+ - [Migrating from 0.3 to 0.4](./working_documents/migration_04.md)
diff --git a/doc/book/src/cookbook/real_world.md b/doc/book/src/cookbook/real_world.md
index 5db8fb70..ba5a7d0e 100644
--- a/doc/book/src/cookbook/real_world.md
+++ b/doc/book/src/cookbook/real_world.md
@@ -11,15 +11,12 @@ to get familiar with Garage's command line and usage patterns.
## Prerequisites
-To run a real-world deployment, make sure you the following conditions are met:
+To run a real-world deployment, make sure the following conditions are met:
- You have at least three machines with sufficient storage space available.
- Each machine has a public IP address which is reachable by other machines.
- Running behind a NAT is possible, but having several Garage nodes behind a single NAT
- is slightly more involved as each will have to have a different RPC port number
- (the local port number of a node must be the same as the port number exposed publicly
- by the NAT).
+ Running behind a NAT is likely to be possible but hasn't been tested for the latest version (TODO).
- Ideally, each machine should have a SSD available in addition to the HDD you are dedicating
to Garage. This will allow for faster access to metadata and has the potential
@@ -45,44 +42,22 @@ For our example, we will suppose the following infrastructure with IPv6 connecti
## Get a Docker image
Our docker image is currently named `lxpz/garage_amd64` and is stored on the [Docker Hub](https://hub.docker.com/r/lxpz/garage_amd64/tags?page=1&ordering=last_updated).
-We encourage you to use a fixed tag (eg. `v0.3.0`) and not the `latest` tag.
-For this example, we will use the latest published version at the time of the writing which is `v0.3.0` but it's up to you
+We encourage you to use a fixed tag (eg. `v0.4.0`) and not the `latest` tag.
+For this example, we will use the latest published version at the time of the writing which is `v0.4.0` but it's up to you
to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/lxpz/garage_amd64/tags?page=1&ordering=last_updated).
For example:
```
-sudo docker pull lxpz/garage_amd64:v0.3.0
+sudo docker pull lxpz/garage_amd64:v0.4.0
```
-
-## Generating TLS certificates
-
-You first need to generate TLS certificates to encrypt traffic between Garage nodes
-(reffered to as RPC traffic).
-
-To generate your TLS certificates, run on your machine:
-
-```
-wget https://git.deuxfleurs.fr/Deuxfleurs/garage/raw/branch/main/genkeys.sh
-chmod +x genkeys.sh
-./genkeys.sh
-```
-
-It will creates a folder named `pki/` containing the keys that you will used for the cluster.
-These files will have to be copied to all of your cluster nodes, as explained below.
-
-
## Deploying and configuring Garage
On each machine, we will have a similar setup,
especially you must consider the following folders/files:
-- `/etc/garage/garage.toml`: Garage daemon's configuration (see below)
-
-- `/etc/garage/pki/`: Folder containing Garage certificates,
- must be generated on your computer and copied on the servers.
- Only the files `garage-ca.crt`, `garage.crt` and `garage.key` are necessary.
+- `/etc/garage.toml`: Garage daemon's configuration (see below)
- `/var/lib/garage/meta/`: Folder containing Garage's metadata,
put this folder on a SSD if possible
@@ -91,7 +66,7 @@ especially you must consider the following folders/files:
this folder will be your main data storage and must be on a large storage (e.g. large HDD)
-A valid `/etc/garage/garage.toml` for our cluster would be:
+A valid `/etc/garage/garage.toml` for our cluster would look as follows:
```toml
metadata_dir = "/var/lib/garage/meta"
@@ -100,18 +75,8 @@ data_dir = "/var/lib/garage/data"
replication_mode = "3"
rpc_bind_addr = "[::]:3901"
-
-bootstrap_peers = [
- "[fc00:1::1]:3901",
- "[fc00:1::2]:3901",
- "[fc00:B::1]:3901",
- "[fc00:F::1]:3901",
-]
-
-[rpc_tls]
-ca_cert = "/etc/garage/pki/garage-ca.crt"
-node_cert = "/etc/garage/pki/garage.crt"
-node_key = "/etc/garage/pki/garage.key"
+rpc_public_addr = "<this node's public IP>:3901"
+rpc_secret = "<RPC secret>"
[s3_api]
s3_region = "garage"
@@ -123,11 +88,14 @@ root_domain = ".web.garage"
index = "index.html"
```
-Please make sure to change `bootstrap_peers` to **your** IP addresses!
+Check the following for your configuration files:
-Check the [configuration file reference documentation](../reference_manual/configuration.md)
-to learn more about all available configuration options.
+- Make sure `rpc_public_addr` contains the public IP address of the node you are configuring.
+ This parameter is optional but recommended: if your nodes have trouble communicating with
+ one another, consider adding it.
+- Make sure `rpc_secret` is the same value on all nodes. It should be a 32-bytes hex-encoded secret key.
+ You can generate such a key with `openssl rand -hex 32`.
## Starting Garage using Docker
@@ -139,11 +107,10 @@ docker run \
--name garaged \
--restart always \
--network host \
- -v /etc/garage/pki:/etc/garage/pki \
- -v /etc/garage/garage.toml:/garage/garage.toml \
+ -v /etc/garage.toml:/etc/garage.toml \
-v /var/lib/garage/meta:/var/lib/garage/meta \
-v /var/lib/garage/data:/var/lib/garage/data \
- lxpz/garage_amd64:v0.3.0
+ lxpz/garage_amd64:v0.4.0
```
It should be restarted automatically at each reboot.
@@ -155,101 +122,102 @@ but please check the relase notes before doing so!
To upgrade, simply stop and remove this container and
start again the command with a new version of Garage.
-
## Controling the daemon
The `garage` binary has two purposes:
- - it acts as a daemon when launched with `garage server ...`
+ - it acts as a daemon when launched with `garage server`
- it acts as a control tool for the daemon when launched with any other command
-In this section, we will see how to use the `garage` binary as a control tool for the daemon we just started.
-You first need to get a shell having access to this binary. For instance, enter the Docker container with:
+Ensure an appropriate `garage` binary (the same version as your Docker image) is available in your path.
+If your configuration file is at `/etc/garage.toml`, the `garage` binary should work with no further change.
+
+You can test your `garage` CLI utility by running a simple command such as:
```bash
-sudo docker exec -ti garaged bash
+garage status
```
-You will now have a shell where the Garage binary is available as `/garage/garage`
-
-*You can also install the binary on your machine to remotely control the cluster.*
-
-## Talk to the daemon and create an alias
-
-`garage` requires 4 options to talk with the daemon:
+At this point, nodes are not yet talking to one another.
+Your output should therefore look like follows:
```
---ca-cert <ca-cert>
---client-cert <client-cert>
---client-key <client-key>
--h, --rpc-host <rpc-host>
+Mercury$ garage node-id
+==== HEALTHY NODES ====
+ID Hostname Address Tag Zone Capacity
+563e1ac825ee3323… Mercury [fc00:1::1]:3901 NO ROLE ASSIGNED
```
-The 3 first ones are certificates and keys needed by TLS, the last one is simply the address of Garage's RPC endpoint.
-If you are invoking `garage` from a server node directly, you do not need to set `--rpc-host`
-as the default value `127.0.0.1:3901` will allow it to contact Garage correctly.
+## Connecting nodes together
+
+When your Garage nodes first start, they will generate a local node identifier
+(based on a public/private key pair).
-To avoid typing the 3 first options each time we want to run a command,
-you can use the following alias:
+To obtain the node identifier of a node, once it is generated,
+run `garage node-id`.
+This will print keys as follows:
```bash
-alias garagectl='/garage/garage \
- --ca-cert /etc/garage/pki/garage-ca.crt \
- --client-cert /etc/garage/pki/garage.crt \
- --client-key /etc/garage/pki/garage.key'
-```
+Mercury$ garage node-id
+563e1ac825ee3323aa441e72c26d1030d6d4414aeb3dd25287c531e7fc2bc95d@[fc00:1::1]:3901
-You can now use all of the commands presented in the [quick start guide](../quick_start/index.md),
-simply replace occurences of `garage` by `garagectl`.
+Venus$ garage node-id
+86f0f26ae4afbd59aaf9cfb059eefac844951efd5b8caeec0d53f4ed6c85f332@[fc00:1::2]:3901
-#### Test the alias
+etc.
+```
-You can test your alias by running a simple command such as:
+You can then instruct nodes to connect to one another as follows:
+```bash
+# Instruct Venus to connect to Mercury (this will establish communication both ways)
+Venus$ garage node connect 563e1ac825ee3323aa441e72c26d1030d6d4414aeb3dd25287c531e7fc2bc95d@[fc00:1::1]:3901
```
-garagectl status
-```
-You should get something like that as result:
+You don't nead to instruct all node to connect to all other nodes:
+nodes will discover one another transitively.
+
+Now if your run `garage status` on any node, you should have an output that looks as follows:
```
-Healthy nodes:
-8781c50c410a41b3… Mercury [fc00:1::1]:3901 UNCONFIGURED/REMOVED
-2a638ed6c775b69a… Venus [fc00:1::2]:3901 UNCONFIGURED/REMOVED
-68143d720f20c89d… Earth [fc00:B::1]:3901 UNCONFIGURED/REMOVED
-212f7572f0c89da9… Mars [fc00:F::1]:3901 UNCONFIGURED/REMOVED
+==== HEALTHY NODES ====
+ID Hostname Address Tag Zone Capacity
+563e1ac825ee3323… Mercury [fc00:1::1]:3901 NO ROLE ASSIGNED
+86f0f26ae4afbd59… Venus [fc00:1::2]:3901 NO ROLE ASSIGNED
+68143d720f20c89d… Earth [fc00:B::1]:3901 NO ROLE ASSIGNED
+212f7572f0c89da9… Mars [fc00:F::1]:3901 NO ROLE ASSIGNED
```
-
-## Configuring a cluster
+## Giving roles to nodes
We will now inform Garage of the disk space available on each node of the cluster
as well as the zone (e.g. datacenter) in which each machine is located.
-For our example, we will suppose we have the following infrastructure (Capacity, Identifier and Datacenter are specific values to Garage described in the following):
+For our example, we will suppose we have the following infrastructure
+(Capacity, Identifier and Zone are specific values to Garage described in the following):
| Location | Name | Disk Space | `Capacity` | `Identifier` | `Zone` |
|----------|---------|------------|------------|--------------|--------------|
-| Paris | Mercury | 1 To | `2` | `8781c5` | `par1` |
-| Paris | Venus | 2 To | `4` | `2a638e` | `par1` |
-| London | Earth | 2 To | `4` | `68143d` | `lon1` |
-| Brussels | Mars | 1.5 To | `3` | `212f75` | `bru1` |
+| Paris | Mercury | 1 To | `2` | `563e` | `par1` |
+| Paris | Venus | 2 To | `4` | `86f0` | `par1` |
+| London | Earth | 2 To | `4` | `6814` | `lon1` |
+| Brussels | Mars | 1.5 To | `3` | `212f` | `bru1` |
#### Node identifiers
After its first launch, Garage generates a random and unique identifier for each nodes, such as:
```
-8781c50c410a41b363167e9d49cc468b6b9e4449b6577b64f15a249a149bdcbc
+563e1ac825ee3323aa441e72c26d1030d6d4414aeb3dd25287c531e7fc2bc95d
```
-Often a shorter form can be used, containing only the beginning of the identifier, like `8781c5`,
+Often a shorter form can be used, containing only the beginning of the identifier, like `563e`,
which identifies the server "Mercury" located in "Paris" according to our previous table.
The most simple way to match an identifier to a node is to run:
```
-garagectl status
+garage status
```
It will display the IP address associated with each node;
@@ -287,16 +255,16 @@ have 66% chance of being stored by Venus and 33% chance of being stored by Mercu
Given the information above, we will configure our cluster as follow:
```
-garagectl node configure -z par1 -c 2 -t mercury 8781c5
-garagectl node configure -z par1 -c 4 -t venus 2a638e
-garagectl node configure -z lon1 -c 4 -t earth 68143d
-garagectl node configure -z bru1 -c 3 -t mars 212f75
+garage node configure -z par1 -c 2 -t mercury 563e
+garage node configure -z par1 -c 4 -t venus 86f0
+garage node configure -z lon1 -c 4 -t earth 6814
+garage node configure -z bru1 -c 3 -t mars 212f
```
## Using your Garage cluster
-Creating buckets and managing keys is done using the `garagectl` CLI,
+Creating buckets and managing keys is done using the `garage` CLI,
and is covered in the [quick start guide](../quick_start/index.md).
Remember also that the CLI is self-documented thanks to the `--help` flag and
the `help` subcommand (e.g. `garage help`, `garage key --help`).
diff --git a/doc/book/src/quick_start/index.md b/doc/book/src/quick_start/index.md
index 5a86ebde..9ff8be7f 100644
--- a/doc/book/src/quick_start/index.md
+++ b/doc/book/src/quick_start/index.md
@@ -10,8 +10,6 @@ Following this guide is recommended before moving on to
Note that this kind of deployment should not be used in production, as it provides
no redundancy for your data!
-We will also skip intra-cluster TLS configuration, meaning that if you add nodes
-to your cluster, communication between them will not be secure.
## Get a binary
@@ -30,7 +28,10 @@ you can [build Garage from source](../cookbook/from_source.md).
## Writing a first configuration file
This first configuration file should allow you to get started easily with the simplest
-possible Garage deployment:
+possible Garage deployment.
+**Save it as `/etc/garage.toml`.**
+You can also store it somewhere else, but you will have to specify `-c path/to/garage.toml`
+at each invocation of the `garage` binary (for example: `garage -c ./garage.toml server`, `garage -c ./garage.toml status`).
```toml
metadata_dir = "/tmp/meta"
@@ -39,10 +40,10 @@ data_dir = "/tmp/data"
replication_mode = "none"
rpc_bind_addr = "[::]:3901"
+rpc_public_addr = "127.0.0.1:3901"
+rpc_secret = "1799bccfd7411eddcf9ebd316bc1f5287ad12a68094e1c6ac6abde7e6feae1ec"
-bootstrap_peers = [
- "127.0.0.1:3901",
-]
+bootstrap_peers = []
[s3_api]
s3_region = "garage"
@@ -54,7 +55,10 @@ root_domain = ".web.garage"
index = "index.html"
```
-Save your configuration file as `garage.toml`.
+The `rpc_secret` value provided above is just an example. It will work, but in
+order to secure your cluster you will need to use another one. You can generate
+such a value with `openssl rand -hex 32`.
+
As you can see in the `metadata_dir` and `data_dir` parameters, we are saving Garage's data
in `/tmp` which gets erased when your system reboots. This means that data stored on this
@@ -67,15 +71,15 @@ your data to be persisted properly.
Use the following command to launch the Garage server with our configuration file:
```
-RUST_LOG=garage=info garage server -c garage.toml
+RUST_LOG=garage=info garage server
```
You can tune Garage's verbosity as follows (from less verbose to more verbose):
```
-RUST_LOG=garage=info garage server -c garage.toml
-RUST_LOG=garage=debug garage server -c garage.toml
-RUST_LOG=garage=trace garage server -c garage.toml
+RUST_LOG=garage=info garage server
+RUST_LOG=garage=debug garage server
+RUST_LOG=garage=trace garage server
```
Log level `info` is recommended for most use cases.
@@ -85,11 +89,12 @@ Log level `debug` can help you check why your S3 API calls are not working.
## Checking that Garage runs correctly
The `garage` utility is also used as a CLI tool to configure your Garage deployment.
-It tries to connect to a Garage server through the RPC protocol, by default looking
-for a Garage server at `localhost:3901`.
+It uses values from the TOML configuration file to find the Garage daemon running on the
+local node, therefore if your configuration file is not at `/etc/garage.toml` you will
+again have to specify `-c path/to/garage.toml`.
-Since our deployment already binds to port 3901, the following command should be sufficient
-to show Garage's status:
+If the `garage` CLI is able to correctly detect the parameters of your local Garage node,
+the following command should be enough to show the status of your cluster:
```
garage status
@@ -98,8 +103,9 @@ garage status
This should show something like this:
```
-Healthy nodes:
-2a638ed6c775b69a… linuxbox 127.0.0.1:3901 UNCONFIGURED/REMOVED
+==== HEALTHY NODES ====
+ID Hostname Address Tag Zone Capacity
+563e1ac825ee3323… linuxbox 127.0.0.1:3901 NO ROLE ASSIGNED
```
## Configuring your Garage node
@@ -117,7 +123,7 @@ garage node configure -z dc1 -c 1 <node_id>
where `<node_id>` corresponds to the identifier of the node shown by `garage status` (first column).
You can enter simply a prefix of that identifier.
-For instance here you could write just `garage node configure -z dc1 -c 1 2a63`.
+For instance here you could write just `garage node configure -z dc1 -c 1 563e`.
diff --git a/doc/book/src/reference_manual/configuration.md b/doc/book/src/reference_manual/configuration.md
index 6c8d5ebc..e165eb87 100644
--- a/doc/book/src/reference_manual/configuration.md
+++ b/doc/book/src/reference_manual/configuration.md
@@ -10,31 +10,26 @@ block_size = 1048576
replication_mode = "3"
+rpc_secret = "4425f5c26c5e11581d3223904324dcb5b5d5dfb14e5e7f35e38c595424f5f1e6"
rpc_bind_addr = "[::]:3901"
+rpc_public_addr = "[fc00:1::1]:3901"
bootstrap_peers = [
- "[fc00:1::1]:3901",
- "[fc00:1::2]:3901",
- "[fc00:B::1]:3901",
- "[fc00:F::1]:3901",
+ "563e1ac825ee3323aa441e72c26d1030d6d4414aeb3dd25287c531e7fc2bc95d@[fc00:1::1]:3901",
+ "86f0f26ae4afbd59aaf9cfb059eefac844951efd5b8caeec0d53f4ed6c85f332[fc00:1::2]:3901",
+ "681456ab91350f92242e80a531a3ec9392cb7c974f72640112f90a600d7921a4@[fc00:B::1]:3901",
+ "212fd62eeaca72c122b45a7f4fa0f55e012aa5e24ac384a72a3016413fa724ff@[fc00:F::1]:3901",
]
consul_host = "consul.service"
consul_service_name = "garage-daemon"
-max_concurrent_rpc_requests = 12
-
sled_cache_capacity = 134217728
sled_flush_every_ms = 2000
-[rpc_tls]
-ca_cert = "/etc/garage/pki/garage-ca.crt"
-node_cert = "/etc/garage/pki/garage.crt"
-node_key = "/etc/garage/pki/garage.key"
-
[s3_api]
-s3_region = "garage"
api_bind_addr = "[::]:3900"
+s3_region = "garage"
[s3_web]
bind_addr = "[::]:3902"
@@ -63,10 +58,15 @@ when [configuring it](../getting_started/05_cluster.md).
#### `block_size`
-Garage splits stored objects in consecutive chunks of size `block_size` (except the last
-one which might be standard). The default size is 1MB and should work in most cases.
-If you are interested in tuning this, feel free to do so (and remember to report your
-findings to us!)
+Garage splits stored objects in consecutive chunks of size `block_size`
+(except the last one which might be smaller). The default size is 1MB and
+should work in most cases. If you are interested in tuning this, feel free
+to do so (and remember to report your findings to us!). If this value is
+changed for a running Garage installation, only files newly uploaded will be
+affected. Previously uploaded files will remain available. This however
+means that chunks from existing files will not be deduplicated with chunks
+from newly uploaded files, meaning you might use more storage space that is
+optimally possible.
#### `replication_mode`
@@ -97,6 +97,14 @@ Never run a Garage cluster where that is not the case.**
Changing the `replication_mode` of a cluster might work (make sure to shut down all nodes
and changing it everywhere at the time), but is not officially supported.
+#### `rpc_secret`
+
+Garage uses a secret key that is shared between all nodes of the cluster
+in order to identify these nodes and allow them to communicate together.
+This key should be specified here in the form of a 32-byte hex-encoded
+random string. Such a string can be generated with a command
+such as `openssl rand -hex 32`.
+
#### `rpc_bind_addr`
The address and port on which to bind for inter-cluster communcations
@@ -106,10 +114,28 @@ the node, even in the case of a NAT: the NAT should be configured to forward the
port number to the same internal port nubmer. This means that if you have several nodes running
behind a NAT, they should each use a different RPC port number.
+#### `rpc_public_addr`
+
+The address and port that other nodes need to use to contact this node for
+RPC calls. **This parameter is optional but recommended.** In case you have
+a NAT that binds the RPC port to a port that is different on your public IP,
+this field might help making it work.
+
#### `bootstrap_peers`
-A list of IPs and ports on which to contact other Garage peers of this cluster.
-This should correspond to the RPC ports set up with `rpc_bind_addr`.
+A list of peer identifiers on which to contact other Garage peers of this cluster.
+These peer identifiers have the following syntax:
+
+```
+<node public key>@<node public IP or hostname>:<port>
+```
+
+In the case where `rpc_public_addr` is correctly specified in the
+configuration file, the full identifier of a node including IP and port can
+be obtained by running `garage node-id` and then included directly in the
+`bootstrap_peers` list of other nodes. Otherwise, only the node's public
+key will be returned by `garage node-id` and you will have to add the IP
+yourself.
#### `consul_host` and `consul_service_name`
@@ -121,12 +147,6 @@ The `consul_host` parameter should be set to the hostname of the Consul server,
and `consul_service_name` should be set to the service name under which Garage's
RPC ports are announced.
-#### `max_concurrent_rpc_requests`
-
-Garage implements rate limiting for RPC requests: no more than
-`max_concurrent_rpc_requests` concurrent outbound RPC requests will be made
-by a Garage node (additionnal requests will be put in a waiting queue).
-
#### `sled_cache_capacity`
This parameter can be used to tune the capacity of the cache used by
@@ -143,21 +163,6 @@ of a power outage (though this should not matter much as data is replicated on o
nodes). The default value, 2000ms, should be appropriate for most use cases.
-## The `[rpc_tls]` section
-
-This section should be used to configure the TLS certificates used to encrypt
-intra-cluster traffic (RPC traffic). The following parameters should be set:
-
-- `ca_cert`: the certificate of the CA that is allowed to sign individual node certificates
-- `node_cert`: the node certificate for the current node
-- `node_key`: the key associated with the node certificate
-
-Note tha several nodes may use the same node certificate, as long as it is signed
-by the CA.
-
-If this section is absent, TLS is not used to encrypt intra-cluster traffic.
-
-
## The `[s3_api]` section
#### `api_bind_addr`
diff --git a/doc/book/src/reference_manual/s3_compatibility.md b/doc/book/src/reference_manual/s3_compatibility.md
index 01059218..272ff41c 100644
--- a/doc/book/src/reference_manual/s3_compatibility.md
+++ b/doc/book/src/reference_manual/s3_compatibility.md
@@ -23,76 +23,35 @@ Not implemented:
All APIs that are not mentionned are not implemented and will return a 400 bad request.
-#### AbortMultipartUpload
-
-Implemented.
-
-#### CompleteMultipartUpload
-
-Implemented badly. Garage will not check that all the parts stored correspond to the list given by the client in the request body. This means that the multipart upload might be completed with an invalid size. This is a bug and will be fixed.
-
-#### CopyObject
-
-Implemented.
-
-#### CreateBucket
-
-Garage does not accept creating buckets or giving access using API calls, it has to be done using the CLI tools. CreateBucket will return a 200 if the bucket exists and user has write access, and a 403 Forbidden in all other cases.
-
-#### CreateMultipartUpload
-
-Implemented.
-
-#### DeleteBucket
-
-Garage does not accept deleting buckets using API calls, it has to be done using the CLI tools. This request will return a 403 Forbidden.
-
-#### DeleteObject
-
-Implemented.
-
-#### DeleteObjects
-
-Implemented.
-
-#### GetBucketLocation
-
-Implemented.
-
-#### GetBucketVersioning
-
-Stub implementation (Garage does not yet support versionning so this always returns
+| Endpoint | Status |
+|------------------------------|----------------------------------|
+| AbortMultipartUpload | Implemented |
+| CompleteMultipartUpload | Implemented |
+| CopyObject | Implemented |
+| CreateBucket | Unsupported, stub (see below) |
+| CreateMultipartUpload | Implemented |
+| DeleteBucket | Unsupported (see below) |
+| DeleteObject | Implemented |
+| DeleteObjects | Implemented |
+| GetBucketLocation | Implemented |
+| GetBucketVersioning | Stub (see below) |
+| GetObject | Implemented |
+| HeadBucket | Implemented |
+| HeadObject | Implemented |
+| ListBuckets | Implemented |
+| ListObjects | Implemented, bugs? (see below) |
+| ListObjectsV2 | Implemented |
+| PutObject | Implemented |
+| UploadPart | Implemented |
+
+
+
+- **CreateBucket:** Garage does not yet accept creating buckets or giving access using API calls, it has to be done using the CLI tools. CreateBucket will return a 200 if the bucket exists and user has write access, and a 403 Forbidden in all other cases.
+
+- **DeleteBucket:** Garage does not yet accept deleting buckets using API calls, it has to be done using the CLI tools. This request will return a 403 Forbidden.
+
+- **GetBucketVersioning:** Stub implementation (Garage does not yet support versionning so this always returns
"versionning not enabled").
-#### GetObject
-
-Implemented.
-
-#### HeadBucket
-
-Implemented.
-
-#### HeadObject
-
-Implemented.
-
-#### ListBuckets
-
-Implemented.
-
-#### ListObjects
-
-Implemented, but there isn't a very good specification of what `encoding-type=url` covers so there might be some encoding bugs. In our implementation the url-encoded fields are in the same in ListObjects as they are in ListObjectsV2.
-
-#### ListObjectsV2
-
-Implemented.
-
-#### PutObject
-
-Implemented.
-
-#### UploadPart
-
-Implemented.
+- **ListObjects:** Implemented, but there isn't a very good specification of what `encoding-type=url` covers so there might be some encoding bugs. In our implementation the url-encoded fields are in the same in ListObjects as they are in ListObjectsV2.
diff --git a/doc/book/src/working_documents/migration_04.md b/doc/book/src/working_documents/migration_04.md
new file mode 100644
index 00000000..b2d98b8a
--- /dev/null
+++ b/doc/book/src/working_documents/migration_04.md
@@ -0,0 +1,61 @@
+# Migrating from 0.3 to 0.4
+
+**Migrating from 0.3 to 0.4 is unsupported. This document is only intended to document the process internally for the Deuxfleurs cluster where we have to do it. Do not try it yourself, you will lose your data and we will not help you.**
+
+**Migrating from 0.2 to 0.4 will break everything for sure. Never try it.**
+
+The internal data format of Garage hasn't changed much between 0.3 and 0.4.
+The Sled database is still the same, and the data directory as well.
+
+The following has changed, all in the meta directory:
+
+- `node_id` in 0.3 contains the identifier of the current node. In 0.4, this file does nothing and should be deleted. It is replaced by `node_key` (the secret key) and `node_key.pub` (the associated public key). A node's identifier on the ring is its public key.
+
+- `peer_info` in 0.3 contains the list of peers saved automatically by Garage. The format has changed and it is now stored in `peer_list` (`peer_info` should be deleted).
+
+When migrating, all node identifiers will change. This also means that the affectation of data partitions on the ring will change, and lots of data will have to be rebalanced.
+
+- If your cluster has only 3 nodes, all nodes store everything, therefore nothing has to be rebalanced.
+
+- If your cluster has only 4 nodes, for any partition there will always be at least 2 nodes that stored data before that still store it after. Therefore the migration should in theory be transparent and Garage should continue to work during the rebalance.
+
+- If your cluster has 5 or more nodes, data will disappear during the migration. Do not migrate (fortunately we don't have this scenario at Deuxfleurs), or if you do, make Garage unavailable until things stabilize (disable web and api access).
+
+
+The migration steps are as follows:
+
+1. Prepare a new configuration file for 0.4. For each node, point to the same meta and data directories as Garage 0.3. Basically, the things that change are the following:
+
+ - No more `rpc_tls` section
+ - You have to generate a shared `rpc_secret` and put it in all config files
+ - `bootstrap_nodes` has a different syntax as it has to contain node keys. Leave it empty and use `garage node-id` and `garage node connect` instead (new features of 0.4)
+ - put the publicly accessible RPC address of your node in `rpc_public_addr` if possible (its optional but recommended)
+ - If you are using Consul, change the `consul_service_name` to NOT be the name advertised by Nomad. Now Garage is responsible for advertising its own service itself.
+
+2. Disable api and web access for some time, do `garage repair --all --yes tables` and `garage repair --all --yes blocks`, check the logs and check that all data seems to be synced correctly between nodes.
+
+3. Save somewhere the output of `garage status`. We will need this to remember how to reconfigure nodes in 0.4.
+
+4. Turn off Garage 0.3
+
+5. Backup metadata folders if you can (i.e. if you have space to do it somewhere). Backuping data folders could also be usefull but that's much harder to do. If your filesystem supports snapshots, this could be a good time to use them.
+
+6. Turn on Garage 0.4
+
+7. At this point, running `garage status` should indicate that all nodes of the previous cluster are "unavailable". The nodes have new identifiers that should appear in healthy nodes once they can talk to one another (use `garage node connect` if necessary`). They should have NO ROLE ASSIGNED at the moment.
+
+8. Prepare a script with several `garage node configure` commands that replace each of the v0.3 node ID with the corresponding v0.4 node ID, with the same zone/tag/capacity. For example if your node `drosera` had identifier `c24e` before and now has identifier `789a`, and it was configured with capacity `2` in zone `dc1`, put the following command in your script:
+
+```bash
+garage node configure 789a -z dc1 -c 2 -t drosera --replace c24e
+```
+
+9. Run your reconfiguration script. Check that the new output of `garage status` contains the correct node IDs with the correct values for capacity and zone. Old nodes should no longer be mentioned.
+
+10. If your cluster has 4 nodes or less, and you are feeling adventurous, you can reenable Web and API access now. Things will probably work.
+
+11. Garage might already be resyncing stuff. Issue a `garage repair --all --yes tables` and `garage repair --all --yes blocks` to force it to do so.
+
+12. Wait for resyncing activity to stop in the logs. Do steps 11 and 12 two or three times, until you see that when you issue the repair commands, nothing gets resynced any longer.
+
+13. Your upgraded cluster should be in a working state. Re-enable API and Web access and check that everything went well.