> ## Content Index
> Fetch the complete content index at: https://www.jannis.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# Trying to get ActivityPub running as well
- URL: https://www.jannis.io/trying-to-get-activitypub-running-as-well/
- Published: 2025-03-02T21:34:48.000Z
- Updated: 2025-03-13T08:34:37.000Z
- Author: Jannis Fedoruk-Betschki

I just read Cathy's blog post about getting ActivityPub started:

[Getting ActivityPub running... well, not yet.![](https://www.jannis.io/content/images/icon/witch-hat-icon_rev-1-01-3.png)Spectral Web ServicesCathy Sarisky, Ghost Expert![](https://www.jannis.io/content/images/thumbnail/ghost-with-dog-food-01.jpg)](https://www.spectralwebservices.com/blog/getting-activitypub-running/?ref=jannis.io)

Since I want to get ActivityPub running on Magic Pages asap, this has been on my list forever – but well...other things always seem to pop up.

Cathy left her blog post with the thought that:

> *Maybe, just maybe, I made this harder than it needed to be by running in WSL and with a local install of Ghost. I may need to take another run at it with a 'real' Linux server, because it feels like I got almost there....*

Well, let's put that to a test. I am running a non-virtualised Ubuntu 24.04 environment. So...maybe things will run better? Follow along:

(I am typing this post as I run the commands on my machine – I am curious how far we get.)

First up, install a fresh copy of Ghost in a new folder:

```
ghost install --local
```

Cool, that worked.

Meanwhile, install Tailscale. For some reason, the command Cathy ran (`curl -fsSL https://tailscale.com/install.sh | sh` and `sudo tailscaled` did not work for me):

```
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/oracular.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/oracular.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list

sudo apt-get update
sudo apt-get install tailscale

sudo tailscale up
```

Then, make sure that Ghost is listening to the Tailscale URL:

```
ghost config url https://YOURINFO.tailYOURINFOHERE.ts.net
```

Ghost reminded me that I should restart Ghost for the config to work, so I did just that:

```
ghost restart
```

Next step, get the ActivityPub server running.

```
git clone https://github.com/TryGhost/ActivityPub.git
cd ActivityPub
yarn dev
```

Docker images are being downloaded – and then started as containers.

But...I run into the same issue as Cathy. The Tailscale URL is not connecting to anything. Looking at the `docker-compose.yml` in the ActivityPub repository, I see that NGINX should be started though.

A `docker ps` shows no NGINX container running though. Hm...

So, I run a `docker logs activitypub-server-nginx-1`:

```
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2025/03/02 19:46:38 [emerg] 1#1: host not found in upstream "host.docker.internal" in /etc/nginx/conf.d/default.conf:15
nginx: [emerg] host not found in upstream "host.docker.internal" in /etc/nginx/conf.d/default.conf:15
```

Okay, this is interesting. The NGINX container couldn't resolve the hostname `host.docker.internal`, which is set in the `nginx/server.conf`. This is makes sense. `host.docker.internal` is a Mac and Windows specific way of telling Docker to access the host. For Linux you'd need to pass this explicitly:

[What is the Linux equivalent of “host.docker.internal”?On Mac and Windows, it is possible to use host.docker.internal (Docker 18.03+) inside a container. Is there one for Linux that will work out of the box without passing environment variables or extr…![](https://www.jannis.io/content/images/icon/apple-touch-icon.png)Stack Overflowklor![](https://www.jannis.io/content/images/thumbnail/apple-touch-icon@2.png)](https://stackoverflow.com/a/62431165?ref=jannis.io)

I have changed the nginx service in the `docker-compose.yml` to this:

```
  nginx:
    build: nginx
    ports:
      - "80:80"
    depends_on:
      - activitypub
    extra_hosts:
      - "host.docker.internal:host-gateway"
```

And tadaaaa...NGINX is running:

```
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
```

Accessing my Tailscale URL I get a 502 error, which is well...better? I mean, before I just didn't get a connection at all. Now, NGINX is telling me that there is no connection to Ghost. So...yay? 😂

I checked with `ghost ls` to make sure Ghost is running. And yes, it is. But...it is listening to the Tailscale URL. That sparks a thought. What if I set the Ghost configuration to have Ghost listen to `0.0.0.0` rather than just `127.0.0.1`?

So, changed the configuration, restarted Ghost, opened the Tailscale URL again and...

![](https://www.jannis.io/content/images/2025/03/image-1.png)

Woohoo 🎉

Now, next step: activate the ActivityPub alpha. For that, developer experiments need to be activated in Ghost's configuration:

```
  "enableDeveloperExperiments": {
    "enableDeveloperExperiments": true
  }
```

Then, another `ghost restart` – and the option appears in Ghost's Labs settings:

![](https://www.jannis.io/content/images/2025/03/image-2.png)

And...you guessed it. Another `ghost restart`. According to the `README.md` of the ActivityPub repository, this should initialise a handshake between Ghost and ActivityPub. Well, let's see what happens.

```
[2025-03-02 21:16:22] INFO Url configured as: http://jannis-laptop.tail11f393.ts.net/
[2025-03-02 21:16:22] INFO Ctrl+C to shut down
[2025-03-02 21:16:22] INFO Ghost server started in 0.242s
[2025-03-02 21:16:23] INFO Database is in a ready state.
[2025-03-02 21:16:23] INFO Ghost database ready in 0.409s
[2025-03-02 21:16:23] INFO Invalidating assets for regeneration
[2025-03-02 21:16:23] INFO Adding offloaded job to the inline job queue
[2025-03-02 21:16:23] INFO Scheduling job mentions-email-report at 53 37 * * * *. Next run on: Sun Mar 02 2025 21:37:53 GMT+0100 (Central European Standard Time)
[2025-03-02 21:16:24] INFO Adding offloaded job to the inline job queue
[2025-03-02 21:16:24] INFO Scheduling job clean-expired-comped at 54 45 4 * * *. Next run on: Mon Mar 03 2025 04:45:54 GMT+0100 (Central European Standard Time)
[2025-03-02 21:16:24] INFO Adding offloaded job to the inline job queue
[2025-03-02 21:16:24] INFO Scheduling job clean-tokens at 14 43 17 * * *. Next run on: Mon Mar 03 2025 17:43:14 GMT+0100 (Central European Standard Time)
[2025-03-02 21:16:24] INFO Ghost booted in 1.512s
[2025-03-02 21:16:24] INFO URL Service ready in 1021ms
[2025-03-02 21:16:24] INFO "GET /ghost/.well-known/jwks.json" 200 10ms
[2025-03-02 21:16:25] ERROR Could not get webhook secret for ActivityPub FetchError: invalid json response body at http://jannis-laptop.tail11f393.ts.net/.ghost/activitypub/site reason: Unexpected token 'I', "Internal S"... is not valid JSON
[2025-03-02 21:16:25] ERROR No webhook secret found - cannot initialise
[2025-03-02 21:16:25] INFO Adding offloaded job to the inline job queue
[2025-03-02 21:16:25] INFO Scheduling job update-check at 22 58 1 * * *. Next run on: Mon Mar 03 2025 01:58:22 GMT+0100 (Central European Standard Time)
[2025-03-02 21:16:25] INFO Running milestone emails job on Sun Mar 02 2025 21:16:30 GMT+0100 (Central European Standard Time)
```

Hm. No webhook secret found? Well, let's check the logs of the ActivityPub stack in Docker:

```
TypeError: fetch failed
Error: connect ECONNREFUSED 100.96.111.34:443
```

Hm. Similar issue to what Cathy saw. SSL isn't set up. Quick search on how to do that with Tailscale (yeah, never used that before). Ran `sudo tailscale cert mytailscaledomain.com`. And...

```
500 Internal Server Error: your Tailscale account does not support getting TLS certs
```

🙃

You need to explicitly enable that here: [https://login.tailscale.com/admin/dns](https://login.tailscale.com/admin/dns?ref=jannis.io)

Bit weird, but hey.

Ran the command again, and got the certificates. Yay! I reconfigured Ghost to listen to the new URL with `https` by running `ghost config url https://myfancynewtailscaleurlwithhttps.com` and restarted it.

...and I got a `ERR_CONNECTION_REFUSED`.

Okay, let's take a step back. Have another look at the `README.md`. Oh. It says `tailscale funnel 80` in there. I just did `tailscale up`. Whoopsie. The `funnel` command makes a lot more sense. It just exposes a single port. Oh well.

So, I ran `tailscale funnel 80` 🤷

Ghost can now initiate the handshake with the ActivityPub server, it seems:

```
[2025-03-02 22:15:32] INFO Checking ActivityPub Webhook state
[2025-03-02 22:15:32] INFO ActivityPub webhooks in correct state
```

Alright. Time to check the new ActivityPub dashboard we have all seen in [Ghost's ActivityPub newsletter](https://activitypub.ghost.org/?ref=jannis.io).

![](https://www.jannis.io/content/images/2025/03/image-4.png)

Wooohooo! 🎉

And as it turns out, I can even find this new ActivityPub instance through Mastodon:

![](https://www.jannis.io/content/images/2025/03/image-5.png)

Now, I can't seem to get see my posts or notes on Mastodon. But uhhh...we have ActivityPub running.

Next step: how the hell will I make this effort scalable for Magic Pages? 😂

---

And while this post shows that it's possible to self-host Ghost's ActivityPub server, I want to stress what the Ghost team published in today's newsletter:

> At the moment we're moving quickly and making many breaking changes each week which aren't backwards compatible (like switching to a new DB) – so the app isn't stable. Even if we did document everything, if you self-hosted then it would just break constantly — so it doesn't make much sense for us to try to document and promote self-hosting, because it won't be a good experience for anyone.

So, do try this at home, I guess. But don't expect it to work in production.