diff --git a/.flaskenv b/.flaskenv
new file mode 100644
index 0000000000000000000000000000000000000000..557b84410e90c35e4130f92151117f37dbf8678a
--- /dev/null
+++ b/.flaskenv
@@ -0,0 +1,2 @@
+FLASK_APP=server.py:app
+FLASK_ENV=development
diff --git a/models/database.py b/models/database.py
index faf5914cd1585099f6faa067528cdd29511c7797..ca5842e22f05aa9cdfc03beafc34fea843ba9c67 100644
--- a/models/database.py
+++ b/models/database.py
@@ -1,4 +1,4 @@
-from flask.ext.login import UserMixin
+from flask_login import UserMixin
 
 from datetime import datetime
 import random
diff --git a/models/forms.py b/models/forms.py
index f9e0be3802efa23b6423d4d9e1ba194906886316..a5ac485859d5091e4832feb0d2a8dc51ee38fab8 100644
--- a/models/forms.py
+++ b/models/forms.py
@@ -1,4 +1,4 @@
-from flask.ext.wtf import Form
+from flask_wtf import Form
 from wtforms import StringField, PasswordField, BooleanField, SelectMultipleField, SelectField, DateField, IntegerField, TextAreaField, HiddenField
 from wtforms.validators import InputRequired, Length, EqualTo, Email, Optional, Length, NumberRange, AnyOf
 from models.database import User
diff --git a/modules/admin.py b/modules/admin.py
index f6803a823580da6b1e1afcbb7a795c464edda530..be99b00e9a503369f5b21601e62356144f8d4369 100644
--- a/modules/admin.py
+++ b/modules/admin.py
@@ -1,5 +1,5 @@
 from flask import Blueprint, redirect, url_for, request, flash, abort, send_file, Response
-from flask.ext.login import login_required
+from flask_login import login_required
 from passlib.hash import pbkdf2_sha256
 
 from datetime import datetime, timedelta
diff --git a/modules/speech.py b/modules/speech.py
index c16bd0fa8b98d90b2a435b594cedde39b718547b..5a5d84bc0ff9ce421c0ed32d07ad4fb500e4c892 100644
--- a/modules/speech.py
+++ b/modules/speech.py
@@ -1,5 +1,5 @@
 from flask import Blueprint, redirect, url_for, request, flash, abort, send_file, Response
-from flask.ext.login import login_required
+from flask_login import login_required
 
 from models.database import User, Statement, Speaker, Topic, Event
 from models.forms import AddStatementForm
diff --git a/requirements.txt b/requirements.txt
index 01606b0e8709a412496eada242283d4c0847573b..9d96b827f04b85c7a8814c670259b8b935fa29e0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,23 +1,25 @@
-alembic==0.9.6
+alembic==1.7.3
 blinker==1.4
-Flask==0.10.1
-Flask-Login==0.3.2
-Flask-Migrate==1.6.0
+click==8.0.1
+Flask==2.0.1
+Flask-Login==0.5.0
+Flask-Migrate==3.1.0
 Flask-Passlib==0.1
 Flask-Principal==0.4.0
-Flask-Script==2.0.5
-Flask-SQLAlchemy==2.1
-Flask-WTF==0.12
-itsdangerous==0.24
-Jinja2==2.8
-Mako==1.0.3
-MarkupSafe==0.23
+Flask-Script==2.0.6
+Flask-SQLAlchemy==2.5.1
+Flask-WTF==0.15.1
+greenlet==1.1.1
+itsdangerous==2.0.1
+Jinja2==3.0.1
+Mako==1.1.5
+MarkupSafe==2.0.1
 passlib==1.6.1
-pkg-resources==0.0.0
-psycopg2==2.7
-python-dateutil==2.8.1
-python-editor==0.4
-six==1.15.0
-SQLAlchemy==1.0.9
-Werkzeug==0.10.4
-WTForms==2.0.2
+psycopg2==2.9.1
+python-dateutil==2.8.2
+python-dotenv==0.19.0
+python-editor==1.0.4
+six==1.16.0
+SQLAlchemy==1.4.23
+Werkzeug==2.0.1
+WTForms==2.3.3
diff --git a/server.py b/server.py
index 542336f75825383b42c70c195e7fff5989e7b50c..b036617e6a8f987a2e67e4ad0c1ada9c57829be5 100755
--- a/server.py
+++ b/server.py
@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 
 from flask import Flask, g, current_app, request, session, flash, redirect, url_for, abort, render_template, Response
-from flask.ext.login import login_user, logout_user, login_required, current_user
-from flask.ext.principal import Principal, Identity, AnonymousIdentity, identity_changed, identity_loaded, UserNeed, RoleNeed
-from flask.ext.script import Manager, prompt, prompt_pass
-from flask.ext.migrate import Migrate, MigrateCommand
+import click
+from flask_login import login_user, logout_user, login_required, current_user
+from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed, identity_loaded, UserNeed, RoleNeed
+from flask_migrate import Migrate
 from passlib.hash import pbkdf2_sha256
 
 import config
@@ -17,8 +17,6 @@ app = Flask(__name__)
 app.config.from_object(config)
 db.init_app(app)
 migrate = Migrate(app, db)
-manager = Manager(app)
-manager.add_command('db', MigrateCommand)
 login_manager.init_app(app)
 login_manager.login_view = "login"
 login_manager.login_message_category = "alert-error"
@@ -30,31 +28,30 @@ from modules import admin, speech
 app.register_blueprint(admin.admin, url_prefix="/admin")
 app.register_blueprint(speech.speech, url_prefix="/speech")
 
-@manager.command
-def addadmin():
+@app.cli.command()
+@click.option("--username", prompt=True)
+@click.option("--realname", prompt=True)
+@click.option("--password", prompt=True, hide_input=True)
+def addadmin(username, realname, password):
     """Add a new administrative user to the system"""
-    print("Adding new administrative user:")
-    admin_real_name = prompt("Real name")
-    admin_login = prompt("Username")
-    admin_pass = prompt_pass("Password")
-    if admin_real_name is not None and admin_login is not None and admin_pass is not None:
-        admin_hashed_pw = pbkdf2_sha256.encrypt(admin_pass, rounds=200000, salt_size=16)
-        u = User(admin_real_name, admin_login, admin_hashed_pw, ["admin", "user"])
+    if username is not None and realname is not None and password is not None:
+        hashed_pw = pbkdf2_sha256.encrypt(password, rounds=200000, salt_size=16)
+        u = User(realname, username, hashed_pw, ["admin", "user"])
         db.session.add(u)
         db.session.commit()
     else:
         print("The provided data was invalid.")
 
-@manager.command
+@app.cli.command()
+@click.option("--username", prompt=True)
+@click.option("--realname", prompt=True)
+@click.option("--password", prompt=True, hide_input=True)
 def adduser():
     """Add a new user to the system"""
     print("Adding new user:")
-    admin_real_name = prompt("Real name")
-    admin_login = prompt("Username")
-    admin_pass = prompt_pass("Password")
-    if admin_real_name is not None and admin_login is not None and admin_pass is not None:
-        admin_hashed_pw = pbkdf2_sha256.encrypt(admin_pass, rounds=200000, salt_size=16)
-        u = User(admin_real_name, admin_login, admin_hashed_pw, ["user"])
+    if realname is not None and username is not None and password is not None:
+        hashed_pw = pbkdf2_sha256.encrypt(password, rounds=200000, salt_size=16)
+        u = User(realname, username, hashed_pw, ["user"])
         db.session.add(u)
         db.session.commit()
     else:
diff --git a/shared.py b/shared.py
index 555920aa87e1e7644b780798eb59133be8f51250..9d3b32f459829cc24d8ec05a8c56e7f1d8c2cc74 100644
--- a/shared.py
+++ b/shared.py
@@ -1,6 +1,6 @@
-from flask.ext.sqlalchemy import SQLAlchemy
-from flask.ext.login import LoginManager
-from flask.ext.principal import Permission, RoleNeed
+from flask_sqlalchemy import SQLAlchemy
+from flask_login import LoginManager
+from flask_principal import Permission, RoleNeed
 
 db = SQLAlchemy()
 login_manager = LoginManager()