Mastodon mit einem Klick auf Pethost deployen
Dein eigener Server im Fediverse: Beiträge, Follows und Antworten über Mastodon hinaus
Was passiert
- Du meldest dich bei Pethost an oder legst ein Konto an, mit Passkey, GitHub oder Google.
- 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.
- „Neues Projekt“ öffnet sich bei der App. Du füllst aus, was sie braucht, und klickst auf „Projekt erstellen“: Benutzername, E-Mail, Admin-Passwort.
- 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.
- 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: