diff --git a/.circleci/config.yml b/.circleci/config.yml
index 8d0c6679b375cc43f46b0b8906c58a738a83306e..c76f6f58f5f2c6efdb7721dc8c389e1a91ad7079 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -17,3 +17,25 @@ jobs:
       - store_artifacts:
           path: bin/import_synapse
           destination: import_synapse
+  sytest:
+    docker:
+      - image: asd
+    working_directory: /go/src/github.com/turt2live/matrix-media-repo
+    steps:
+      # Build the project
+      - checkout
+      - run: go get github.com/constabulary/gb/...
+      - run: gb vendor restore
+      - run: gb build all
+      # Install sytest
+      - run: cd /test
+      - run: wget -q https://github.com/matrix-org/sytest/archive/develop.tar.gz -O sytest.tar.gz
+      - run: tar --strip-components=1 -xf sytest.tar.gz
+      # Finally, run the tests
+      - run: ./run-tests.sh
+workflows:
+  version: 2
+  build_and_test:
+    jobs:
+      - build
+      - sytest
diff --git a/Dockerfile-sytest b/Dockerfile-sytest
new file mode 100644
index 0000000000000000000000000000000000000000..e809a3477c9a6e872f03f88c646af869797a3659
--- /dev/null
+++ b/Dockerfile-sytest
@@ -0,0 +1,20 @@
+FROM matrixdotorg/sytest:latest
+
+RUN apt-get -qq install -y curl nginx dos2unix \
+    && curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz \
+    && tar xvf go1.10.2.linux-amd64.tar.gz \
+    && chown -R root:root ./go \
+    && mv go /usr/local \
+    && mkdir /go \
+    && openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" \
+    && openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
+
+COPY docker/sytest/site.conf /etc/nginx/sites-available/default
+COPY docker/sytest/run-tests.sh /test/run-tests.sh
+COPY docker/sytest/media-repo.yaml /test/media-repo.yaml
+COPY docker/sytest/03ascii.patch /test/03ascii.patch
+
+RUN dos2unix /test/run-tests.sh
+
+ENV GOPATH=/go
+ENV PATH="${PATH}:/usr/local/go/bin:/go/bin"
\ No newline at end of file
diff --git a/docker/sytest/03ascii.patch b/docker/sytest/03ascii.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5ddfcb4d2e9066a1083bc806de1c14086a3ae5ab
--- /dev/null
+++ b/docker/sytest/03ascii.patch
@@ -0,0 +1,52 @@
+--- tests/51media/03ascii.pl    2018-11-07 11:28:39.000000000 +0000
++++ tests/51media/03ascii_mediaonly.pl  2018-11-11 03:50:11.642993500 +0000
+@@ -97,49 +97,3 @@
+       });
+    };
+
+-test "Can send image in room message",
+-   requires => [ $main::API_CLIENTS[0], local_user_and_room_fixtures() ],
+-
+-   check => sub {
+-      my ( $http, $user, $room_id ) = @_;
+-      test_using_client( $http )
+-      ->then( sub {
+-         matrix_send_room_message( $user, $room_id,
+-            content => { msgtype => "m.file", body => "test.txt", url => $content_uri }
+-         )
+-      });
+-   };
+-
+-test "Can fetch images in room",
+-   requires => [ $main::API_CLIENTS[0], local_user_and_room_fixtures() ],
+-
+-   check => sub {
+-      my ( $http, $user, $room_id ) = @_;
+-      test_using_client( $http )
+-      ->then( sub {
+-         matrix_send_room_message_synced( $user, $room_id,
+-            content => { msgtype => "m.text", body => "test" }
+-         )
+-      })->then( sub {
+-         matrix_send_room_message_synced( $user, $room_id,
+-            content => { msgtype => "m.file", body => "test.txt", url => $content_uri }
+-         )
+-      })->then( sub {
+-         do_request_json_for( $user,
+-            method => "GET",
+-            uri    => "/api/v1/rooms/$room_id/messages",
+-            params => {
+-               filter => '{"contains_url":true}',
+-               dir    => 'b',
+-            }
+-         )
+-      })->then( sub {
+-         my ( $body ) = @_;
+-
+-         assert_json_keys( $body, qw( start end chunk ));
+-
+-         assert_eq( scalar @{ $body->{chunk} }, 1, "Expected 1 message" );
+-
+-         Future->done( 1 );
+-      });
+-   };
diff --git a/docker/sytest/run-tests.sh b/docker/sytest/run-tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..b49e0353d0410c030341abd511b8bfa151e1c178
--- /dev/null
+++ b/docker/sytest/run-tests.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+cd /test
+/go/src/github.com/turt2live/matrix-media-repo/bin/media_repo -config /test/media-repo.yaml -migrations /go/src/github.com/turt2live/matrix-media-repo/migrations & pid_mr=$!
+/go/src/github.com/turt2live/matrix-media-repo/bin/sytest_homeserver & pid_hs=$!
+nginx
+
+mkdir -p /tmp/mr/uploads
+mkdir -p /tmp/mr/logs
+
+export PGDATA=/var/lib/postgresql/data
+su -c '/usr/lib/postgresql/9.6/bin/initdb -E "UTF-8" --lc-collate="en_US.UTF-8" --lc-ctype="en_US.UTF-8" --username=postgres' postgres
+su -c '/usr/lib/postgresql/9.6/bin/pg_ctl -w -D /var/lib/postgresql/data start' postgres
+su -c 'psql -c "CREATE DATABASE mediarepo;"' postgres
+
+patch tests/51media/03ascii.pl 03ascii.patch
+./run-tests.pl -I Manual -L https://localhost --server-name example.org tests/51media/*
+
+kill -9 $pid_mr
+kill -9 $pid_hs
+nginx -s stop
diff --git a/docker/sytest/site.conf b/docker/sytest/site.conf
new file mode 100644
index 0000000000000000000000000000000000000000..061915cebb8b95914bb18255b11d79a94754b403
--- /dev/null
+++ b/docker/sytest/site.conf
@@ -0,0 +1,47 @@
+server {
+    listen 80 default_server;
+    listen [::]:80 default_server;
+
+    return 302 https://localhost$request_uri;
+}
+
+server {
+    listen 443 ssl default_server;
+    listen [::]:443 ssl default_server;
+
+    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
+    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
+
+    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+    ssl_prefer_server_ciphers on;
+    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
+    ssl_ecdh_curve secp384r1;
+    ssl_session_cache shared:SSL:10m;
+    ssl_session_tickets off;
+    ssl_stapling on;
+    ssl_stapling_verify on;
+    resolver 8.8.8.8 8.8.4.4 valid=300s;
+    resolver_timeout 5s;
+
+    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
+    add_header X-Frame-Options DENY;
+    add_header X-Content-Type-Options nosniff;
+
+    ssl_dhparam /etc/ssl/certs/dhparam.pem;
+
+    location / {
+        proxy_read_timeout 600s;
+        proxy_set_header Host example.org;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_pass http://localhost:8000;
+    }
+
+    location /_matrix/client {
+        proxy_read_timeout 600s;
+        proxy_set_header Host example.org;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_pass http://localhost:8001;
+    }
+}
\ No newline at end of file
diff --git a/src/github.com/turt2live/matrix-media-repo/cmd/sytest_homeserver/main.go b/src/github.com/turt2live/matrix-media-repo/cmd/sytest_homeserver/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..de9cf30f6a2323327fbf0cc6f361e10aa4e895aa
--- /dev/null
+++ b/src/github.com/turt2live/matrix-media-repo/cmd/sytest_homeserver/main.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/gorilla/mux"
+	"github.com/turt2live/matrix-media-repo/util"
+)
+
+// This is a limited in-memory homeserver used for the purposes of facilitating the tests
+
+type user struct {
+	localpart string
+	domain    string
+	mxid      string
+}
+
+var accessTokenMap = make(map[string]user)
+
+func main() {
+	rtr := mux.NewRouter()
+	rtr.Handle("/_matrix/client/r0/register", register{}).Methods("POST")
+	rtr.Handle("/_matrix/client/r0/account/whoami", whoami{}).Methods("GET")
+
+	http.Handle("/", rtr)
+	http.ListenAndServe("localhost:8001", nil)
+}
+
+type registerResponse struct {
+	UserID      string `json:"user_id"`
+	AccessToken string `json:"access_token"`
+	DeviceID    string `json:"device_id"`
+}
+type registerRequest struct {
+	Username string `json:"username"`
+}
+
+type register struct{}
+
+func (c register) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	decoder := json.NewDecoder(r.Body)
+	var t registerRequest
+	err := decoder.Decode(&t)
+	if err != nil {
+		panic(err)
+	}
+
+	token, err := util.GenerateRandomString(32)
+	if err != nil {
+		panic(err)
+	}
+
+	accessTokenMap[token] = user{
+		localpart: t.Username,
+		domain:    "example.org",
+		mxid:      fmt.Sprintf("@%s:example.org", t.Username),
+	}
+
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(http.StatusOK)
+
+	encoder := json.NewEncoder(w)
+	encoder.Encode(registerResponse{
+		UserID:      accessTokenMap[token].mxid,
+		AccessToken: token,
+		DeviceID:    "example",
+	})
+}
+
+type whoamiResponse struct {
+	UserID string `json:"user_id"`
+}
+
+type whoami struct{}
+
+func (c whoami) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	token := r.Header.Get("Authorization")
+	token = token[len("Bearer "):]
+
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(http.StatusOK)
+
+	encoder := json.NewEncoder(w)
+	encoder.Encode(whoamiResponse{
+		UserID: accessTokenMap[token].mxid,
+	})
+}