diff --git a/.gitignore b/.gitignore
index d68e7dcf419c1863ff41582642ac0ff2e2964699..a690686765ab7b22f64eda9c085f2ba3fccd696b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ __pycache__
 htmlcov
 .pytest_cache
 /.idea
-.vs/
\ No newline at end of file
+.vs/
+*/static
\ No newline at end of file
diff --git a/server/app/__init__.py b/server/app/__init__.py
index 48a104f83ffd051af99dc24b5c528b3057aa480f..8aa7a08a36108f683361768752aaf3e0f47e737e 100644
--- a/server/app/__init__.py
+++ b/server/app/__init__.py
@@ -1,7 +1,9 @@
 from flask import Flask, redirect, request
+from flask_uploads import IMAGES, UploadSet, configure_uploads
 
 import app.database.models as models
 from app.core import bcrypt, db, jwt, ma
+from app.core.dto import MediaDTO
 
 
 def create_app(config_name="configmodule.DevelopmentConfig"):
@@ -14,6 +16,7 @@ def create_app(config_name="configmodule.DevelopmentConfig"):
         jwt.init_app(app)
         db.init_app(app)
         ma.init_app(app)
+        configure_uploads(app, (MediaDTO.image_set,))
 
         from app.apis import flask_api
 
diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py
index 996c14bea098059678ac53028c28cced40853416..3b2ed213ac140a49096aeee16cdd3924a76ae30d 100644
--- a/server/app/apis/__init__.py
+++ b/server/app/apis/__init__.py
@@ -44,6 +44,7 @@ from flask_restx import Api
 
 from .auth import api as auth_ns
 from .competitions import api as comp_ns
+from .media import api as media_ns
 from .misc import api as misc_ns
 from .questions import api as question_ns
 from .slides import api as slide_ns
@@ -51,6 +52,7 @@ from .teams import api as team_ns
 from .users import api as user_ns
 
 flask_api = Api()
+flask_api.add_namespace(media_ns, path="/api/media")
 flask_api.add_namespace(misc_ns, path="/api/misc")
 flask_api.add_namespace(user_ns, path="/api/users")
 flask_api.add_namespace(auth_ns, path="/api/auth")
@@ -58,4 +60,4 @@ flask_api.add_namespace(comp_ns, path="/api/competitions")
 flask_api.add_namespace(slide_ns, path="/api/competitions/<CID>/slides")
 flask_api.add_namespace(team_ns, path="/api/competitions/<CID>/teams")
 flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/questions")
-#flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/slides/<SID>/question")
+# flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/slides/<SID>/question")
diff --git a/server/app/apis/admin.py b/server/app/apis/admin.py
deleted file mode 100644
index 92d3922ca1ee1324566ce886a50155ab31ca8e1d..0000000000000000000000000000000000000000
--- a/server/app/apis/admin.py
+++ /dev/null
@@ -1,8 +0,0 @@
-###
-# Admin stuff placed here for later use
-# No need to implement this before the application is somewhat done
-###
-
-from flask import Blueprint
-
-admin_blueprint = Blueprint("admin", __name__)
diff --git a/server/app/apis/components.py b/server/app/apis/components.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/server/app/apis/media.py b/server/app/apis/media.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7c2d5d143963e87d434d4c6c7770427663622c0
--- /dev/null
+++ b/server/app/apis/media.py
@@ -0,0 +1,47 @@
+import app.core.http_codes as codes
+import app.database.controller as dbc
+from app.apis import admin_required, item_response, list_response
+from app.core.dto import MediaDTO
+from app.core.parsers import media_parser_search
+from app.database.models import City, Media, MediaType, QuestionType, Role
+from flask import request
+from flask_jwt_extended import get_jwt_identity, jwt_required
+from flask_restx import Resource, reqparse
+from flask_uploads import UploadNotAllowed
+from PIL import Image
+
+api = MediaDTO.api
+image_set = MediaDTO.image_set
+schema = MediaDTO.schema
+list_schema = MediaDTO.list_schema
+
+
+def generate_thumbnail(filename):
+    with Image.open(f"./static/images/{filename}") as im:
+        im.thumbnail((120, 120))
+        im.save(f"./static/images/thumbnail_{filename}")
+
+
+@api.route("/images")
+class ImageList(Resource):
+    @jwt_required
+    def get(self):
+        args = media_parser_search.parse_args(strict=True)
+        items, total = dbc.search.image(**args)
+        return list_response(list_schema.dump(items), total)
+
+    @jwt_required
+    def post(self):
+        if "image" not in request.files:
+            api.abort(codes.BAD_REQUEST, "Missing image in request.files")
+
+        try:
+            filename = image_set.save(request.files["image"])
+            generate_thumbnail(filename)
+            print(filename)
+            item = dbc.add.image(filename, get_jwt_identity())
+            return item_response(schema.dump(item))
+        except UploadNotAllowed:
+            api.abort(codes.BAD_REQUEST, "Could not save the image")
+        except:
+            api.abort(codes.INTERNAL_SERVER_ERROR, "Something went wrong when trying to save image")
diff --git a/server/app/core/dto.py b/server/app/core/dto.py
index e2f36bbbb33d2f4a8163c05f0ba5bfcca7a23ea6..99d467ba7251962de5de2ec24ed1cbb150d001ed 100644
--- a/server/app/core/dto.py
+++ b/server/app/core/dto.py
@@ -2,6 +2,14 @@ import app.core.rich_schemas as rich_schemas
 import app.core.schemas as schemas
 import marshmallow as ma
 from flask_restx import Namespace, fields
+from flask_uploads import IMAGES, UploadSet
+
+
+class MediaDTO:
+    api = Namespace("media")
+    image_set = UploadSet("photos", IMAGES)
+    schema = schemas.MediaSchema(many=False)
+    list_schema = schemas.MediaSchema(many=True)
 
 
 class AuthDTO:
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index f05adc10831d72a1fb97389166660a6eb1bfb7b0..7a1c6089c1893a01d70b858e13097eb84a74029b 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -64,3 +64,7 @@ question_parser.add_argument("type_id", type=int, default=None, location="json")
 ###TEAM####
 team_parser = reqparse.RequestParser()
 team_parser.add_argument("name", type=str, location="json")
+
+###SEARCH_COMPETITION####
+media_parser_search = search_parser.copy()
+media_parser_search.add_argument("filename", type=str, default=None, location="args")
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index 37f34dd731c7af5ba6016eedf0b095d970d113fb..f612cdd67a858cc68b5ec2ca952089c2a77bd756 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -4,6 +4,7 @@ from app.database.models import (
     Blacklist,
     City,
     Competition,
+    Media,
     MediaType,
     Question,
     QuestionType,
@@ -35,6 +36,11 @@ def blacklist(jti):
     return Blacklist(jti)
 
 
+@db_add
+def image(filename, user_id):
+    return Media(filename, 1, user_id)
+
+
 @db_add
 def slide(item_competition):
     order = Slide.query.filter(Slide.competition_id == item_competition.id).count()  # first element has index 0
diff --git a/server/app/database/controller/search.py b/server/app/database/controller/search.py
index a2ca6b842377cba5d523ea1374de39757e3ddb4b..466efd01ceab68f316f63299fff73cddff2bfd9d 100644
--- a/server/app/database/controller/search.py
+++ b/server/app/database/controller/search.py
@@ -1,4 +1,12 @@
-from app.database.models import Competition, Question, Slide, Team, User
+from app.database.models import Competition, Media, Question, Slide, Team, User
+
+
+def image(filename, page=0, page_size=15, order=1, order_by=None):
+    query = Media.query.filter(Media.type_id == 1)
+    if filename:
+        query = query.filter(Media.filename.like(f"%{filename}%"))
+
+    return query.pagination(page, page_size, None, None)
 
 
 def user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15, order=1, order_by=None):
@@ -74,10 +82,7 @@ def questions(
     if slide_id:
         query = query.filter(Question.slide_id == slide_id)
     if competition_id:
-        slide_ids = set(
-            [x.id for x in Slide.query.filter(Slide.competition_id == competition_id).all()]
-        )  # TODO: Filter using database instead of creating a set of slide_ids
-        query = query.filter(Question.slide_id.in_(slide_ids))
+        query = query.join(Slide, (Slide.competition_id == competition_id) & (Slide.id == Question.slide_id))
 
     order_column = Question.id  # Default order_by
     if order_by:
diff --git a/server/app/database/models.py b/server/app/database/models.py
index c3bc3d56cef44c65df080d7a9ae97afbb9e03b93..5a17fb0d7bd28353827c3e9cb97049292ac7ef77 100644
--- a/server/app/database/models.py
+++ b/server/app/database/models.py
@@ -1,5 +1,6 @@
 from app.core import bcrypt, db
 from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
+from sqlalchemy.orm import backref
 
 STRING_SIZE = 254
 
@@ -90,10 +91,13 @@ class Competition(db.Model):
     year = db.Column(db.Integer, nullable=False, default=2020)
 
     city_id = db.Column(db.Integer, db.ForeignKey("city.id"), nullable=False)
+    background_image_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True)
 
     slides = db.relationship("Slide", backref="competition")
     teams = db.relationship("Team", backref="competition")
 
+    background_image = db.relationship("Media", uselist=False)
+
     def __init__(self, name, year, city_id):
         self.name = name
         self.year = year
@@ -123,6 +127,9 @@ class Slide(db.Model):
     settings = db.Column(db.Text, nullable=False, default="{}")
     competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False)
 
+    background_image_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True)
+    background_image = db.relationship("Media", uselist=False)
+
     def __init__(self, order, competition_id):
         self.order = order
         self.competition_id = competition_id
diff --git a/server/configmodule.py b/server/configmodule.py
index d042d594bf1dea8c81081c701d7b87d2ddc196c9..fcf23cbf5624e977be6cb3f3f27aae3d173a0528 100644
--- a/server/configmodule.py
+++ b/server/configmodule.py
@@ -1,22 +1,26 @@
+import os
 from datetime import timedelta
 
 
 class Config:
     DEBUG = False
     TESTING = False
+    BUNDLE_ERRORS = True
     SQLALCHEMY_DATABASE_URI = "sqlite:///database.db"
+    SQLALCHEMY_TRACK_MODIFICATIONS = False
     JWT_SECRET_KEY = "super-secret"
     JWT_BLACKLIST_ENABLED = True
     JWT_BLACKLIST_TOKEN_CHECKS = ["access", "refresh"]
-    BUNDLE_ERRORS = True
     JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=2)
     JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
-    SQLALCHEMY_TRACK_MODIFICATIONS = False
+    UPLOADED_PHOTOS_DEST = "static/images"  # os.getcwd()
+    SECRET_KEY = os.urandom(24)
 
 
 class DevelopmentConfig(Config):
     DEBUG = True
     SQLALCHEMY_DATABASE_URI = "sqlite:///database.db"
+    SQLALCHEMY_ECHO = True
 
 
 class TestingConfig(Config):
diff --git a/server/requirements.txt b/server/requirements.txt
index 172f152510c3ccd6245f19e5cb53ac702a6b4370..cbda8a7040167ce59a2209747e8d7d4204f7fe2d 100644
Binary files a/server/requirements.txt and b/server/requirements.txt differ