diff --git a/README.md b/README.md
index 6d1aede68..211759e1f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/LemmyNet/lemmy.svg)
-![Build Status](https://cloud.drone.io/api/badges/LemmyNet/lemmy/status.svg)
+[![Build Status](https://travis-ci.org/LemmyNet/lemmy.svg?branch=main)](https://travis-ci.org/LemmyNet/lemmy)
[![GitHub issues](https://img.shields.io/github/issues-raw/LemmyNet/lemmy.svg)](https://github.com/LemmyNet/lemmy/issues)
[![Docker Pulls](https://img.shields.io/docker/pulls/dessalines/lemmy.svg)](https://cloud.docker.com/repository/docker/dessalines/lemmy/)
[![Translation status](http://weblate.yerbamate.ml/widgets/lemmy/-/lemmy/svg-badge.svg)](http://weblate.yerbamate.ml/engage/lemmy/)
diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml
new file mode 100644
index 000000000..dc015a289
--- /dev/null
+++ b/docker/federation/docker-compose.yml
@@ -0,0 +1,237 @@
+version: '3.3'
+
+services:
+ nginx:
+ image: nginx:1.17-alpine
+ ports:
+ - "8540:8540"
+ - "8550:8550"
+ - "8560:8560"
+ - "8570:8570"
+ - "8580:8580"
+ volumes:
+ - ./nginx.conf:/etc/nginx/nginx.conf
+ restart: on-failure
+ depends_on:
+ - pictrs
+ - iframely
+ - lemmy-alpha-ui
+ - lemmy-beta-ui
+ - lemmy-gamma-ui
+ - lemmy-delta-ui
+ - lemmy-epsilon-ui
+
+ pictrs:
+ restart: always
+ image: asonix/pictrs:v0.2.5-r0
+ user: 991:991
+ volumes:
+ - ./volumes/pictrs_alpha:/mnt
+
+ lemmy-alpha-ui:
+ image: dessalines/lemmy-ui:v0.8.10
+ environment:
+ - LEMMY_INTERNAL_HOST=lemmy-alpha:8541
+ - LEMMY_EXTERNAL_HOST=localhost:8541
+ - LEMMY_HTTPS=false
+ depends_on:
+ - lemmy-alpha
+ lemmy-alpha:
+ image: lemmy-federation:latest
+ environment:
+ - LEMMY_HOSTNAME=lemmy-alpha:8541
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon
+ - LEMMY_PORT=8541
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-alpha
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_alpha
+ ports:
+ - "8541:8541"
+ postgres_alpha:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_alpha:/var/lib/postgresql/data
+
+ lemmy-beta-ui:
+ image: dessalines/lemmy-ui:v0.8.10
+ environment:
+ - LEMMY_INTERNAL_HOST=lemmy-beta:8551
+ - LEMMY_EXTERNAL_HOST=localhost:8551
+ - LEMMY_HTTPS=false
+ depends_on:
+ - lemmy-beta
+ lemmy-beta:
+ image: lemmy-federation:latest
+ environment:
+ - LEMMY_HOSTNAME=lemmy-beta:8551
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon
+ - LEMMY_PORT=8551
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-beta
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_beta
+ ports:
+ - "8551:8551"
+ postgres_beta:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_beta:/var/lib/postgresql/data
+
+ lemmy-gamma-ui:
+ image: dessalines/lemmy-ui:v0.8.10
+ environment:
+ - LEMMY_INTERNAL_HOST=lemmy-gamma:8561
+ - LEMMY_EXTERNAL_HOST=localhost:8561
+ - LEMMY_HTTPS=false
+ depends_on:
+ - lemmy-gamma
+ lemmy-gamma:
+ image: lemmy-federation:latest
+ environment:
+ - LEMMY_HOSTNAME=lemmy-gamma:8561
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_gamma:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon
+ - LEMMY_PORT=8561
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-gamma
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_gamma
+ ports:
+ - "8561:8561"
+ postgres_gamma:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_gamma:/var/lib/postgresql/data
+
+ # An instance with only an allowlist for beta
+ lemmy-delta-ui:
+ image: dessalines/lemmy-ui:v0.8.10
+ environment:
+ - LEMMY_INTERNAL_HOST=lemmy-delta:8571
+ - LEMMY_EXTERNAL_HOST=localhost:8571
+ - LEMMY_HTTPS=false
+ depends_on:
+ - lemmy-delta
+ lemmy-delta:
+ image: lemmy-federation:latest
+ environment:
+ - LEMMY_HOSTNAME=lemmy-delta:8571
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_delta:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta
+ - LEMMY_PORT=8571
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-delta
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_delta
+ ports:
+ - "8571:8571"
+ postgres_delta:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_delta:/var/lib/postgresql/data
+
+ # An instance who has a blocklist, with lemmy-alpha blocked
+ lemmy-epsilon-ui:
+ image: dessalines/lemmy-ui:v0.8.10
+ environment:
+ - LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
+ - LEMMY_EXTERNAL_HOST=localhost:8581
+ - LEMMY_HTTPS=false
+ depends_on:
+ - lemmy-epsilon
+ lemmy-epsilon:
+ image: lemmy-federation:latest
+ environment:
+ - LEMMY_HOSTNAME=lemmy-epsilon:8581
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_epsilon:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha
+ - LEMMY_PORT=8581
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-epsilon
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_epsilon
+ ports:
+ - "8581:8581"
+ postgres_epsilon:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_epsilon:/var/lib/postgresql/data
+
+ iframely:
+ image: dogbin/iframely:latest
+ volumes:
+ - ../iframely.config.local.js:/iframely/config.local.js:ro
+ restart: always
diff --git a/docker/federation/nginx.conf b/docker/federation/nginx.conf
new file mode 100644
index 000000000..357b87c90
--- /dev/null
+++ b/docker/federation/nginx.conf
@@ -0,0 +1,230 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ upstream lemmy-alpha {
+ server "lemmy-alpha:8541";
+ }
+ upstream lemmy-alpha-ui {
+ server "lemmy-alpha-ui:1234";
+ }
+ server {
+ listen 8540;
+ server_name 127.0.0.1;
+ access_log off;
+
+ # Upload limit for pictshare
+ client_max_body_size 50M;
+
+ location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
+ proxy_pass http://lemmy-alpha;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ location / {
+ set $proxpass http://lemmy-alpha-ui;
+ if ($http_accept = "application/activity+json") {
+ set $proxpass http://lemmy-alpha;
+ }
+ if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+ set $proxpass http://lemmy-alpha;
+ }
+ proxy_pass $proxpass;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # Cuts off the trailing slash on URLs to make them valid
+ rewrite ^(.+)/+$ $1 permanent;
+ }
+ location /iframely/ {
+ proxy_pass http://iframely:80/;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+
+ upstream lemmy-beta {
+ server "lemmy-beta:8551";
+ }
+ upstream lemmy-beta-ui {
+ server "lemmy-beta-ui:1234";
+ }
+ server {
+ listen 8550;
+ server_name 127.0.0.1;
+ access_log off;
+
+ # Upload limit for pictshare
+ client_max_body_size 50M;
+
+ location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
+ proxy_pass http://lemmy-beta;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ location / {
+ set $proxpass http://lemmy-beta-ui;
+ if ($http_accept = "application/activity+json") {
+ set $proxpass http://lemmy-beta;
+ }
+ if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+ set $proxpass http://lemmy-beta;
+ }
+ proxy_pass $proxpass;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # Cuts off the trailing slash on URLs to make them valid
+ rewrite ^(.+)/+$ $1 permanent;
+ }
+ location /iframely/ {
+ proxy_pass http://iframely:80/;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+
+ upstream lemmy-gamma {
+ server "lemmy-gamma:8561";
+ }
+ upstream lemmy-gamma-ui {
+ server "lemmy-gamma-ui:1234";
+ }
+ server {
+ listen 8560;
+ server_name 127.0.0.1;
+ access_log off;
+
+ # Upload limit for pictshare
+ client_max_body_size 50M;
+
+ location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
+ proxy_pass http://lemmy-gamma;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ location / {
+ set $proxpass http://lemmy-gamma-ui;
+ if ($http_accept = "application/activity+json") {
+ set $proxpass http://lemmy-gamma;
+ }
+ if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+ set $proxpass http://lemmy-gamma;
+ }
+ proxy_pass $proxpass;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # Cuts off the trailing slash on URLs to make them valid
+ rewrite ^(.+)/+$ $1 permanent;
+ }
+ location /iframely/ {
+ proxy_pass http://iframely:80/;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+
+ upstream lemmy-delta {
+ server "lemmy-delta:8571";
+ }
+ upstream lemmy-delta-ui {
+ server "lemmy-delta-ui:1234";
+ }
+ server {
+ listen 8570;
+ server_name 127.0.0.1;
+ access_log off;
+
+ # Upload limit for pictshare
+ client_max_body_size 50M;
+
+ location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
+ proxy_pass http://lemmy-delta;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ location / {
+ set $proxpass http://lemmy-delta-ui;
+ if ($http_accept = "application/activity+json") {
+ set $proxpass http://lemmy-delta;
+ }
+ if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+ set $proxpass http://lemmy-delta;
+ }
+ proxy_pass $proxpass;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # Cuts off the trailing slash on URLs to make them valid
+ rewrite ^(.+)/+$ $1 permanent;
+ }
+ location /iframely/ {
+ proxy_pass http://iframely:80/;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+
+ upstream lemmy-epsilon {
+ server "lemmy-epsilon:8581";
+ }
+ upstream lemmy-epsilon-ui {
+ server "lemmy-epsilon-ui:1234";
+ }
+ server {
+ listen 8580;
+ server_name 127.0.0.1;
+ access_log off;
+
+ # Upload limit for pictshare
+ client_max_body_size 50M;
+
+ location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
+ proxy_pass http://lemmy-epsilon;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ location / {
+ set $proxpass http://lemmy-epsilon-ui;
+ if ($http_accept = "application/activity+json") {
+ set $proxpass http://lemmy-epsilon;
+ }
+ if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+ set $proxpass http://lemmy-epsilon;
+ }
+ proxy_pass $proxpass;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # Cuts off the trailing slash on URLs to make them valid
+ rewrite ^(.+)/+$ $1 permanent;
+ }
+ location /iframely/ {
+ proxy_pass http://iframely:80/;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+}
diff --git a/docker/federation/run-tests.bash b/docker/federation/run-tests.bash
new file mode 100755
index 000000000..03f18d7e6
--- /dev/null
+++ b/docker/federation/run-tests.bash
@@ -0,0 +1,31 @@
+#!/bin/bash
+set -e
+
+# make sure there are no old containers or old data around
+sudo docker-compose down
+sudo rm -rf volumes
+
+mkdir -p volumes/pictrs_{alpha,beta,gamma,delta,epsilon}
+sudo chown -R 991:991 volumes/pictrs_{alpha,beta,gamma,delta,epsilon}
+
+sudo docker build ../../ --file ../dev/Dockerfile --tag lemmy-federation:latest
+
+sudo mkdir -p volumes/pictrs_alpha
+sudo chown -R 991:991 volumes/pictrs_alpha
+
+sudo docker-compose up -d
+
+pushd ../../api_tests
+echo "Waiting for Lemmy to start..."
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8551/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8561/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8571/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8581/api/v1/site')" != "200" ]]; do sleep 1; done
+yarn
+yarn api-test || true
+popd
+
+sudo docker-compose down
+
+sudo rm -r volumes
diff --git a/docker/federation/start-local-instances.bash b/docker/federation/start-local-instances.bash
new file mode 100755
index 000000000..ec2712f14
--- /dev/null
+++ b/docker/federation/start-local-instances.bash
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -e
+
+sudo docker build ../../ --file ../dev/volume_mount.dockerfile -t lemmy-federation:latest
+
+for Item in alpha beta gamma delta epsilon ; do
+ sudo mkdir -p volumes/pictrs_$Item
+ sudo chown -R 991:991 volumes/pictrs_$Item
+done
+
+sudo docker-compose up -d
+
+echo "Waiting for Lemmy to start..."
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8551/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8561/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8571/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8581/api/v1/site')" != "200" ]]; do sleep 1; done
+echo "All instances started."
+
+sudo docker-compose logs -f
diff --git a/docker/prod/deploy.sh b/docker/prod/deploy.sh
index 1206f8c96..81bfdab8e 100755
--- a/docker/prod/deploy.sh
+++ b/docker/prod/deploy.sh
@@ -19,16 +19,22 @@ cd docker/prod || exit
# Changing various references to the Lemmy version
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../dev/docker-compose.yml
+sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../federation/docker-compose.yml
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../prod/docker-compose.yml
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../prod/docker-compose.yml
+sed -i "s/dessalines\/lemmy:v.*/dessalines\/lemmy:$new_tag/" ../travis/docker_push.sh
git add ../dev/docker-compose.yml
+git add ../federation/docker-compose.yml
git add ../prod/docker-compose.yml
+git add ../travis/docker_push.sh
# The commit
git commit -m"Version $new_tag"
git tag $new_tag
+# Now doing the building on travis, but leave this in for when you need to do an arm build
+
# export COMPOSE_DOCKER_CLI_BUILD=1
# export DOCKER_BUILDKIT=1
diff --git a/docker/travis/docker-compose.yml b/docker/travis/docker-compose.yml
new file mode 100644
index 000000000..565f7a000
--- /dev/null
+++ b/docker/travis/docker-compose.yml
@@ -0,0 +1,159 @@
+version: '3.3'
+
+services:
+ lemmy-alpha:
+ image: dessalines/lemmy:travis
+ environment:
+ - LEMMY_HOSTNAME=lemmy-alpha:8541
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon
+ - LEMMY_PORT=8541
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-alpha
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_alpha
+ ports:
+ - "8541:8541"
+ postgres_alpha:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_alpha:/var/lib/postgresql/data
+
+ lemmy-beta:
+ image: dessalines/lemmy:travis
+ environment:
+ - LEMMY_HOSTNAME=lemmy-beta:8551
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon
+ - LEMMY_PORT=8551
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-beta
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_beta
+ ports:
+ - "8551:8551"
+ postgres_beta:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_beta:/var/lib/postgresql/data
+
+ lemmy-gamma:
+ image: dessalines/lemmy:travis
+ environment:
+ - LEMMY_HOSTNAME=lemmy-gamma:8561
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_gamma:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon
+ - LEMMY_PORT=8561
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-gamma
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_gamma
+ ports:
+ - "8561:8561"
+ postgres_gamma:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_gamma:/var/lib/postgresql/data
+
+ # An instance with only an allowlist for beta
+ lemmy-delta:
+ image: dessalines/lemmy:travis
+ environment:
+ - LEMMY_HOSTNAME=lemmy-delta:8571
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_delta:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta
+ - LEMMY_PORT=8571
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-delta
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_delta
+ ports:
+ - "8571:8571"
+ postgres_delta:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_delta:/var/lib/postgresql/data
+
+ # An instance who has a blocklist, with lemmy-alpha blocked
+ lemmy-epsilon:
+ image: dessalines/lemmy:travis
+ environment:
+ - LEMMY_HOSTNAME=lemmy-epsilon:8581
+ - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_epsilon:5432/lemmy
+ - LEMMY_JWT_SECRET=changeme
+ - LEMMY_FEDERATION__ENABLED=true
+ - LEMMY_TLS_ENABLED=false
+ - LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha
+ - LEMMY_PORT=8581
+ - LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon
+ - LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+ - LEMMY_SETUP__SITE_NAME=lemmy-epsilon
+ - LEMMY_RATE_LIMIT__POST=99999
+ - LEMMY_RATE_LIMIT__REGISTER=99999
+ - LEMMY_CAPTCHA__ENABLED=false
+ - RUST_BACKTRACE=1
+ - RUST_LOG=debug
+ depends_on:
+ - postgres_epsilon
+ ports:
+ - "8581:8581"
+ postgres_epsilon:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_USER=lemmy
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=lemmy
+ volumes:
+ - ./volumes/postgres_epsilon:/var/lib/postgresql/data
diff --git a/docker/travis/docker_push.sh b/docker/travis/docker_push.sh
new file mode 100644
index 000000000..ba77f0264
--- /dev/null
+++ b/docker/travis/docker_push.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
+docker tag dessalines/lemmy:travis \
+ dessalines/lemmy:v0.8.10
+docker push dessalines/lemmy:v0.8.10
diff --git a/docker/travis/run-tests.bash b/docker/travis/run-tests.bash
new file mode 100755
index 000000000..01460d30a
--- /dev/null
+++ b/docker/travis/run-tests.bash
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -e
+
+# make sure there are no old containers or old data around
+sudo docker-compose down
+sudo rm -rf volumes
+
+mkdir -p volumes/pictrs_{alpha,beta,gamma,delta,epsilon}
+sudo chown -R 991:991 volumes/pictrs_{alpha,beta,gamma,delta,epsilon}
+
+sudo docker build ../../ --file ../prod/Dockerfile --tag dessalines/lemmy:travis
+
+sudo docker-compose up -d
+
+pushd ../../api_tests
+echo "Waiting for Lemmy to start..."
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8551/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8561/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8571/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8581/api/v1/site')" != "200" ]]; do sleep 1; done
+yarn
+yarn api-test
+popd
+
+sudo docker-compose down
+
+sudo rm -r volumes/
diff --git a/lemmy_apub/src/fetcher.rs b/lemmy_apub/src/fetcher.rs
index c26f1464c..4e1fa98a6 100644
--- a/lemmy_apub/src/fetcher.rs
+++ b/lemmy_apub/src/fetcher.rs
@@ -95,7 +95,7 @@ enum SearchAcceptedObjects {
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
///
-/// Some working examples for use with the `api_tests` setup:
+/// Some working examples for use with the `docker/federation/` setup:
/// http://lemmy_alpha:8541/c/main, or !main@lemmy_alpha:8541
/// http://lemmy_beta:8551/u/lemmy_alpha, or @lemmy_beta@lemmy_beta:8551
/// http://lemmy_gamma:8561/post/3