Português
Começar
← Todos os templates

Faça o deploy de Mastodon no Pethost com um clique

Seu próprio servidor no fediverso: posts, seguidores e respostas além do Mastodon

O que acontece

  1. Você entra no Pethost ou cria uma conta, com uma passkey, GitHub ou Google.
  2. Ainda não tem máquina? Você escolhe um plano, e nós preparamos uma para você. O link espera no seu navegador até ela ficar pronta.
  3. Novo projeto abre no app. Você preenche o que ele pede e clica em Criar projeto: Usuário, E-mail, Senha do administrador.
  4. Em um ou dois minutos o app está no ar no próprio endereço com HTTPS. Os segredos dele são gerados ao acaso, e os dados vão para o backup noturno.
  5. É um projeto como qualquer outro: o compose.yaml, o .env, os logs e um terminal ficam no painel, e seu agente pode alterá-lo, fazer rollback ou restaurá-lo.

Primeiros passos

Entre com o e-mail e a senha que você informou: você é o dono do servidor, com o nome de usuário que você informou. O e-mail precisa receber mensagens: o Mastodon verifica o domínio dele e, sem isso, o deploy falha. O Mastodon reserva nomes como admin ou root. Os cadastros estão fechados; abra-os em Preferências, Administração, Configurações do servidor. O Mastodon não envia e-mails até que SMTP_SERVER, SMTP_LOGIN, SMTP_PASSWORD e SMTP_FROM_ADDRESS estejam no .env do projeto e o projeto reinicie. Cada conta fica ligada a este endereço para sempre, então dê ao projeto o endereço definitivo antes de publicar. As mídias enviadas ficam guardadas em /opt/mastodon/public/system.

O que roda

O template é este compose.yaml e as variáveis que ele grava no .env do projeto. O Pethost roda o arquivo exatamente como está escrito.

# 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: