diff --git a/machines/biblios/garage.nix b/machines/biblios/garage.nix
index 20b3822..b7109c1 100644
--- a/machines/biblios/garage.nix
+++ b/machines/biblios/garage.nix
@@ -21,6 +21,12 @@ in
         api_bind_addr = "[::]:3900";
         root_domain = ".${api_domain}";
       };
+      s3_web = {
+        bind_addr = "127.0.0.1:3902";
+        root_domain = ".cdn.luj.fr";
+        index = "index.html";
+      };
+
       rpc_bind_addr = "[::]:3901";
       rpc_public_addr = "127.0.0.1:3901";
 
@@ -43,4 +49,23 @@ in
       '';
     };
   };
+
+  services.nginx.virtualHosts."cdn.luj.fr" = {
+    enableACME = true;
+    forceSSL = true;
+    serverAliases = [ "cdn.social.luj.fr" ];
+    locations."/".extraConfig = ''
+      proxy_pass http://127.0.0.1:3902;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header Host $host;
+    '';
+  };
+
+  machine.meta.zones."luj.fr".subdomains."social".subdomains."cdn".A = [
+    config.machine.meta.ips.public.ipv4
+  ];
+  machine.meta.zones."luj.fr".subdomains."social".subdomains."cdn".AAAA = [
+    config.machine.meta.ips.public.ipv6
+  ];
+
 }
diff --git a/machines/gustave/default.nix b/machines/gustave/default.nix
index 418cffd..370a90c 100644
--- a/machines/gustave/default.nix
+++ b/machines/gustave/default.nix
@@ -15,6 +15,7 @@
     ./readeck.nix
     ./plausible.nix
     ./nextcloud.nix
+    ./mastodon.nix
   ];
 
   machine.meta = {
diff --git a/machines/gustave/mastodon.nix b/machines/gustave/mastodon.nix
new file mode 100644
index 0000000..f5a6c08
--- /dev/null
+++ b/machines/gustave/mastodon.nix
@@ -0,0 +1,41 @@
+{ config, ... }:
+
+{
+
+  age.secrets."mastodon-env".file = ../../secrets/mastodon-env.age;
+
+  services.mastodon = {
+    enable = true;
+    localDomain = "social.luj.fr";
+    configureNginx = true;
+    extraConfig.SINGLE_USER_MODE = "true";
+    streamingProcesses = 10;
+    extraConfig = {
+      OIDC_ENABLED = "true";
+      OIDC_DISPLAY_NAME = "Luj - SSO";
+      OIDC_DISCOVERY = "true";
+      OIDC_ISSUER = "https://auth.luj.fr/oauth2/openid/mastodon";
+      OIDC_SCOPE = "openid,profile,email";
+      OIDC_UID_FIELD = "email";
+      OIDC_CLIENT_ID = "mastodon";
+      OIDC_REDIRECT_URI = "https://social.luj.fr/auth/auth/openid_connect/callback";
+      OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED = "true";
+      ONE_CLICK_SSO_LOGIN = "true";
+
+      # S3
+      S3_ENABLED = "true";
+      S3_BUCKET = "mastodon";
+      S3_REGION = "paris";
+      S3_ENDPOINT = "https://s3.luj.fr";
+      S3_HOSTNAME = "s3.luj.fr";
+      S3_ALIAS_HOST = "cdn.social.luj.fr";
+      SMTP_SERVER = "mail.luj.fr";
+      SMTP_PORT = "587";
+      SMTP_FROM_ADDRESS = "infra@luj.fr";
+      SMTP_LOGIN = "luj";
+    };
+    extraEnvFiles = [ config.age.secrets."mastodon-env".path ];
+
+  };
+
+}
diff --git a/secrets/mastodon-env.age b/secrets/mastodon-env.age
new file mode 100644
index 0000000..d5a0eb3
Binary files /dev/null and b/secrets/mastodon-env.age differ
diff --git a/secrets/secrets.nix b/secrets/secrets.nix
index 85b401b..d94c717 100644
--- a/secrets/secrets.nix
+++ b/secrets/secrets.nix
@@ -168,4 +168,10 @@ in
     gustave
   ];
 
+  "mastodon-env.age".publicKeys = [
+    tower
+    gallifrey
+    gustave
+  ];
+
 }