---
title: "Mastodon mit einem Klick auf Pethost deployen"
description: "Dein eigener Server im Fediverse: Beiträge, Follows und Antworten über Mastodon hinaus. Eine Vorlage für Pethost: In ein, zwei Minuten läuft die App auf deiner Maschine, mit HTTPS unter eigener Adresse und nächtlichen Backups."
---

[← Alle Vorlagen](https://pethost.dev/de/templates/)

# Mastodon mit einem Klick auf Pethost deployen

Dein eigener Server im Fediverse: Beiträge, Follows und Antworten über Mastodon hinaus

[Auf Pethost deployen](https://console.pethost.dev/?template=mastodon)

## Was passiert

1. Du meldest dich bei Pethost an oder legst ein Konto an, mit Passkey, GitHub oder Google.
2. Noch keine Maschine? Du wählst einen Tarif, und wir bereiten eine für dich vor. Der Link wartet in deinem Browser, bis sie bereit ist.
3. „Neues Projekt“ öffnet sich bei der App. Du füllst aus, was sie braucht, und klickst auf „Projekt erstellen“: **Benutzername**, **E-Mail**, **Admin-Passwort**.
4. In ein, zwei Minuten läuft die App unter eigener Adresse mit HTTPS. Ihre Secrets werden zufällig für sie erzeugt, und ihre Daten sind im nächtlichen Backup.
5. Sie ist ein Projekt wie jedes andere: compose.yaml, .env, Logs und ein Terminal findest du im Panel, und dein Agent kann sie ändern, zurückrollen oder wiederherstellen.

## Erste Schritte

Melde dich mit der angegebenen E-Mail und dem Passwort an: Du bist der Owner des Servers, mit dem angegebenen Nutzernamen. Die E-Mail muss Post empfangen können: Mastodon prüft ihre Domain, sonst schlägt der Deploy fehl. Namen wie admin oder root reserviert Mastodon. Registrierungen sind geschlossen; öffne sie unter Einstellungen, Administration, Server-Einstellungen. Mastodon verschickt keine E-Mails, bis SMTP_SERVER, SMTP_LOGIN, SMTP_PASSWORD und SMTP_FROM_ADDRESS in der .env des Projekts stehen und das Projekt neu startet. Jedes Konto ist für immer an diese Adresse gebunden, also gib dem Projekt seine endgültige Adresse, bevor du postest. Hochgeladene Medien bleiben in /opt/mastodon/public/system erhalten.

## Was läuft

Die Vorlage ist diese compose.yaml und die Variablen, die sie in die .env des Projekts schreibt. Pethost führt die Datei so aus, wie sie geschrieben ist.

```
# Mastodon's docker-compose.yml with a small Caddy in front, which gives the streaming
# API its path on the project's address, as Mastodon's nginx does.
x-env: &env
  LOCAL_DOMAIN: ${HOST}
  DB_HOST: db
  DB_PORT: "5432"
  DB_USER: mastodon
  DB_NAME: mastodon
  DB_PASS: ${DB_PASSWORD}
  REDIS_HOST: redis
  REDIS_PORT: "6379"
  SECRET_KEY_BASE: ${SECRET_KEY_BASE}
  ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: ${AR_DETERMINISTIC_KEY}
  ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: ${AR_KEY_DERIVATION_SALT}
  ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: ${AR_PRIMARY_KEY}
  VAPID_SEED: ${VAPID_SEED}
  ES_ENABLED: "false"
  WEB_CONCURRENCY: "1"
  SMTP_SERVER: ${SMTP_SERVER:-}
  SMTP_PORT: ${SMTP_PORT:-587}
  SMTP_LOGIN: ${SMTP_LOGIN:-}
  SMTP_PASSWORD: ${SMTP_PASSWORD:-}
  SMTP_FROM_ADDRESS: ${SMTP_FROM_ADDRESS:-}

# Web push needs a VAPID key pair. Its private key is the 32 random bytes of VAPID_SEED,
# and the public key is derived from it, so web and sidekiq read the same pair and
# nothing is kept apart from .env.
x-vapid: &vapid |
  eval "$$(ruby -ropenssl -rbase64 -e '
    d = [ENV.fetch("VAPID_SEED")].pack("H*")
    point = OpenSSL::PKey::EC::Group.new("prime256v1").generator.mul(OpenSSL::BN.new(d, 2))
    puts "export VAPID_PRIVATE_KEY=#{Base64.urlsafe_encode64(d)}"
    puts "export VAPID_PUBLIC_KEY=#{Base64.urlsafe_encode64(point.to_octet_string(:uncompressed))}"
  ')"

services:
  proxy:
    image: caddy:2.10-alpine
    restart: unless-stopped
    configs:
      - source: caddy
        target: /etc/caddy/Caddyfile
    depends_on:
      - web
      - streaming

  web:
    image: ghcr.io/mastodon/mastodon:v4.7.3
    restart: unless-stopped
    environment:
      <<: *env
      ADMIN_USERNAME: ${ADMIN_USERNAME}
      ADMIN_EMAIL: ${ADMIN_EMAIL}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      VAPID: *vapid
    # Every start migrates the database and seeds what is missing; the first one also
    # makes the owner from the username, email and password given. Registrations stay
    # closed, Mastodon's default, so nobody else can sign up until the owner opens them.
    command:
      - sh
      - -c
      - |
        set -e
        eval "$$VAPID"
        bundle exec rails db:prepare db:seed
        bundle exec rails runner '
          owner = UserRole.find_by!(name: "Owner")
          unless User.exists?(role_id: owner.id)
            password = ENV.fetch("ADMIN_PASSWORD")
            user = User.create!(
              email: ENV.fetch("ADMIN_EMAIL"),
              password: password,
              password_confirmation: password,
              account: Account.new(username: ENV.fetch("ADMIN_USERNAME")),
              role: owner,
              confirmed_at: Time.now.utc,
              agreement: true,
              approved: true,
              bypass_registration_checks: true
            )
            # Closed registrations leave a new user waiting for approval.
            user.approve!
          end
        '
        exec bundle exec puma -C config/puma.rb
    volumes:
      - system:/opt/mastodon/public/system
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -s --noproxy localhost localhost:3000/health | grep -q OK"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 300s

  streaming:
    image: ghcr.io/mastodon/mastodon-streaming:v4.7.3
    restart: unless-stopped
    environment: *env
    command: node ./streaming/index.js
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -s --noproxy localhost localhost:4000/api/v1/streaming/health | grep -q OK"]
      interval: 10s
      timeout: 5s
      retries: 5

  sidekiq:
    image: ghcr.io/mastodon/mastodon:v4.7.3
    restart: unless-stopped
    environment:
      <<: *env
      VAPID: *vapid
    command:
      - sh
      - -c
      - |
        eval "$$VAPID"
        exec bundle exec sidekiq
    volumes:
      - system:/opt/mastodon/public/system
    depends_on:
      web:
        condition: service_healthy

  db:
    image: postgres:17
    restart: unless-stopped
    shm_size: 256mb
    environment:
      POSTGRES_USER: mastodon
      POSTGRES_DB: mastodon
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "mastodon"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7.4
    restart: unless-stopped
    volumes:
      - redis:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 10

configs:
  # The machine's proxy is trusted for X-Forwarded-Proto, so Rails sees HTTPS.
  caddy:
    content: |
      {
        admin off
        auto_https off
        servers {
          trusted_proxies static private_ranges
        }
      }

      :8080 {
        reverse_proxy /api/v1/streaming* streaming:4000
        reverse_proxy web:3000
      }

volumes:
  system:
  db:
  redis:
```

[compose.yaml](https://pethost.dev/templates/mastodon/compose.yaml)[Auf GitHub ansehen](https://github.com/pethost-dev/templates/tree/main/mastodon)

Pethost · Stephan Gomer · NIP 1133096755

[Vorlagen](https://pethost.dev/de/templates/) [Über uns](https://pethost.dev/about/) [Blog](https://pethost.dev/blog/) [AGB](https://pethost.dev/terms/) [Datenschutz](https://pethost.dev/privacy/) [Erstattung](https://pethost.dev/refunds/) [API](https://pethost.dev/docs/api/) [CLI](https://pethost.dev/docs/cli/) [Plugin](https://github.com/pethost-dev/plugin) [support@pethost.dev](mailto:support@pethost.dev)
