A Mastodon compatible (Activity Pub) bot instance developed in PHP to quickly create and deploy multiple bots. I use the bots to regularly post interesting content (historical photos) to Mastodon.
Manual registration at large instances (like Mastodon.social) was slow: register -> verify -> create app -> add scopes... This tool helps to deploy bots (new ActivityPub accounts / identities) in couple of clicks.
See it in action: https://bots.ambience.sk/
- Pure PHP, no external dependencies.
- Apache, nginx or Docker - your choice.
- SQLite used as a database for low friction setup (database = single file).
- Setup a domain/subdomain with SSL certificate, where your ActivityPub (Mastodon compatible) instance will run, e.g.
bots.example.comorexample.com. - Download the code or
git clone https://github.com/nekromoff/mastodon-bots(to the root of domain/subdomain). - Continue to Setup your first bot below...
Just upload the code and it works.
nginx.conf reproduces the full routing table and the access denials (lib/, data/, no PHP execution inside uploads/). Copy it, adjust server_name, root, fastcgi_pass, and the certificate paths, then:
nginx -t && systemctl reload nginxA ready-to-run image (Alpine + Apache + PHP 8.4 + SQLite) is included. DOMAIN_NAME is the only required setting:
DOMAIN_NAME=bots.example.com docker compose up -dOr without compose:
docker build -t mastodon-bot .
docker run -d --name mastodon-bot -p 8080:80 \
-e DOMAIN_NAME=bots.example.com \
-v mastodon-bot-data:/var/www/html/data \
-v mastodon-bot-uploads:/var/www/html/uploads \
mastodon-botThe database and uploads live in volumes, so they survive image rebuilds. Migrations run automatically on every start, which also upgrades an existing volume in place. Put TLS in front (reverse proxy or tunnel) and forward X-Forwarded-Proto so the app generates https:// URLs and marks its session cookie Secure.
| Variable | Default | Purpose |
|---|---|---|
DOMAIN_NAME |
— | Public hostname the instance federates under (required) |
DISPLAY_TIMEZONE |
UTC |
Timezone for timestamps shown in the admin UI |
PHP_MEMORY_LIMIT |
128M |
PHP memory limit |
PHP_UPLOAD_MAX_FILESIZE |
40M |
Maximum media upload size |
HTTP_PORT |
8080 |
Host port (compose only) |
DOMAIN_NAME is baked into actor URIs, activity ids, and HTTP signature key ids. Changing it after the instance has federated will break references remote servers already hold — the bootstrap warns when it detects such a change.
php tests/run.phpCovers HTTP signature verification, SSRF protection, admin XSS escaping, replay protection, timestamp handling, and authentication. See tests/README.md.
Multiuser (multibot) Fediverse instance. I recommend to host it on a domain or a subdomain. index.php is used to handle all traffic via rewrites.
Public facing website with list of bots and their posts including their profiles and full Mastodon compatible feeds.
Automatically generated based on the bots and their activity. Fully indexable by Google and other search engines. Verification via rel="me" included as well as bot creator META tag.
- Full administration panel - create and manage your bots (multiple bots per instance)
- Login: single admin user - authenticated via password only
- Bots - quick overview of bot activity and their stats
- create a bot
- edit a bot
- post as a bot
- social features - follow or block
- move - migration features - move from (alsoKnownAs) or to (movedTo) an instance
- Logs - log of all relevant events (incoming and outgoing) for all bots, filterable to failures only
- Settings - instance setup, log retention, media limits, delivery health
Very simple API to communicate with your bots.
- Create a post
- Upload some media
- Post with media (use ID from upload response)
- Edit a post (but you can also do that manually via Admin)
- Delete a post (but you can also do that manually via Admin)
- Follow a remote account
- Open your instance at
example.com/admin/and set it up including admin password. - Log in using your admin password.
- Create New Bot, set username (no @ character), display name, short bio, password for the bot (keep it for later = API access)
- Edit your bot to enter additonal details (extra fields, featured hashtags) and upload your profile icon (avatar) and a header image. Set options such as discoverability, featuring in Mastodon explore, search engine indexing, followers approval. All options preset for full visibility.
- Go to Settings and change log retention, media upload limits and the dead-inbox
grace period as needed. An inbox that answers every delivery with a timeout, DNS or TLS
failure, 400/404/410 or 5xx for the whole grace period (7 days by default) has its
followers sent a
Rejectof their originalFollowand dropped, and any relay subscription on it parked as dead. One successful delivery clears the record entirely. Rate limits (429) and signature rejections (401/403) never count, and pruning pauses while most inboxes are failing at once — that pattern points at a local fault. - See API Usage under settings for simple API communication with your bots.
Yes, no dependencies mean no need for
composer. SQLite database is just a file indata/folder, so no need to set up a separate database.
Yes, definitely. Just setup an SSL certificate and upload this code and you are good to go.
Once you are done with a first time setup, you can create a bot in about 20 seconds. You will need some more time to edit bio, upload profile image etc.
Yes, I built it to do just that!
API requests authenticate using bot username and password. You can use a custom script to post any content to Mastodon (ActivityPub). The API is very simple.
Example (creating a Mastodon / ActivityPub post):
curl -u botname:botpassword \
-X POST https://example.com/api/post \
-H "Content-Type: application/json" \
-d '{"content":"Hello Fediverse! #test","visibility":"public"}'
Yes, there is an admin UI to manually create, edit and delete posts.
Yes, although I wouldn't recommend it. There is no way for users (other than admin) to log in. All users would share common admin interface with a single password.
Note: Computer-assisted process was used to develop this code.
