<aside> 💡 This guide has been improved and moved to the urbit.org website.



By ~tomnyr-tirsyr


Adding S3 storage to Urbit unlocks some great new features, such as the ability to post media to chats and upload custom avatars. Unfortunately (and rather ironically) the Urbit docs only describe the process of renting S3 storage from MEGACORPs. This is a guide to self hosting MinIO, an S3 compatible block storage solution.

This is assuming you already have a machine/VPS to host on as well as a domain - we will use [example.com](<http://example.com>) for this guide. Specifically, we will use the subdomain [s3.example.com](<http://s3.example.com>) to point to our MinIO installation. You should substitute your own domain everywhere the example domain is used in this guide.

Ideally, MinIO would be installed on the same machine that your Urbit planet is hosted on, and run along side at no extra cost - although it can just as easily be hosted on a separate machine.




Set up time 30min - 1hr

  1. Install and run MinIO via Docker. This is the simplest way to get MinIO up and running, though if you prefer you can install and run MinIO as a standalone without using Docker. This guide assumes the Docker method.

    MinIO | Learn more about MinIO's Docker Implementation

    Be sure to add the MINIO_DOMAIN environment variable; this tells MinIO to accept virtual host style URLs — <bucket>.s3.example.com rather than s3.example.com/<bucket>. Your final command will look something like this:

    docker run -d -p 9000:9000 \\
      --name minio1 \\
      -v /mnt/data:/data \\
      -e "MINIO_ACCESS_KEY=<access_key>" \\
      -e "MINIO_SECRET_KEY=<secret_key>" \\
    	-e "MINIO_DOMAIN=s3.example.com" \\
      minio/minio server /data

    /mnt/data is the path where uploaded files will be stored on your machine. Change this to suit your own environment.

    AFAIK your access key & secret key can be anything of your choosing — make sure they're secure! ~ribben-donnyl let me know that there is a handy tool called minio-keygen that will generate secure keys for you. You could also use your @p and +code for an easy to remember solution.

  2. Create 2 DNS A records, both pointing at the IP of the machine you are hosting MinIO on:

    It's important that you create both — without the wildcard, you'll be able to access your S3 web interface but not upload/download content from any buckets you create.

  3. Aquire a certificate using Let's Encrypt that covers your MinIO URL and the wildcard subdomain of that URL — in this instance that is [s3.example.com](<http://s3.example.com>) and *.s3.example.com. This way we can have TLS for the main endpoint and the endpoint of all buckets we create.

    sudo certbot --server <https://acme-v02.api.letsencrypt.org/directory> -d *.s3.example.com -d s3.example.com --manual --preferred-challenges dns-01 certonly

    After successfully completing the DNS challenge, your certificate files will be generated and certbot will print the paths at which they are located. Take note of these for the next step.

    <aside> 💡 Don't forget to renew your certificates! If they expire, your browser will most likely refuse to connect to your services. Use the same command again to generate new certs, and then point your webserver to them.


  4. Install the NGINX web server and place the following config file at /etc/nginx/sites-enabled/s3.example.com. NGINX will act as a proxy to MinIO, allowing us to use our domain and enable TLS.

    server {
      listen 443 ssl;
      server_name *.s3.example.com;
      ignore_invalid_headers off;
      client_max_body_size 0; # Set to a value such as 1000m; to restrict file size to a specific value
      proxy_buffering off;
      ssl_certificate /etc/letsencrypt/live/s3.example.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/s3.example.com/privkey.pem;
      include /etc/letsencrypt/options-ssl-nginx.conf;
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 
      location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_pass <http://localhost:9000>;
    server {
      listen 80;
      server_name *.s3.example.com;
    	if ($host = *.s3.example.com) {
    	  return 301 https://$host$request_uri;
    	return 404;

    Update your server_name values, ensure that the paths to your certificate files are correct, and change your proxy_pass value if you are running MinIO on a different port.

  5. Navigate to your root MinIO endpoint (https://s3.example.com) in a browser and sign in and create a new bucket. In the list of buckets on the left hand side of the interface, you should see an option to change the policy to read/write.

  6. Head over to Landscape and enter your root MinIO endpoint (with protocol) in the S3 settings, e.g. https://s3.example.com. Enter your access key and secret, and then enter the name of the bucket you created in the previous step.

  7. You should now be able to post media in chat, completely self hosted!