diff --git a/web/app/__init__.py b/web/app/__init__.py
index 7a5d8b6850c2f3e48dc30227e017801f5f5254ba..a39a51a55576aa65c4bcf433a98696f8a66e76a6 100644
--- a/web/app/__init__.py
+++ b/web/app/__init__.py
@@ -28,22 +28,23 @@ def create_app(config_name):
     socketio.init_app(
         app, message_queue=config[config_name].SOCKETIO_MESSAGE_QUEUE_URI)
 
-    from . import events
-    from .admin import admin as admin_blueprint
+    with app.app_context():
+        from . import events
+        from .admin import admin as admin_blueprint
+        from .auth import auth as auth_blueprint
+        from .corpora import corpora as corpora_blueprint
+        from .errors import errors as errors_blueprint
+        from .jobs import jobs as jobs_blueprint
+        from .main import main as main_blueprint
+        from .services import services as services_blueprint
+        from .settings import settings as settings_blueprint
     app.register_blueprint(admin_blueprint, url_prefix='/admin')
-    from .auth import auth as auth_blueprint
     app.register_blueprint(auth_blueprint, url_prefix='/auth')
-    from .corpora import corpora as corpora_blueprint
     app.register_blueprint(corpora_blueprint, url_prefix='/corpora')
-    from .errors import errors as errors_blueprint
     app.register_blueprint(errors_blueprint)
-    from .jobs import jobs as jobs_blueprint
     app.register_blueprint(jobs_blueprint, url_prefix='/jobs')
-    from .main import main as main_blueprint
     app.register_blueprint(main_blueprint)
-    from .profile import profile as profile_blueprint
-    app.register_blueprint(profile_blueprint, url_prefix='/profile')
-    from .services import services as services_blueprint
     app.register_blueprint(services_blueprint, url_prefix='/services')
+    app.register_blueprint(settings_blueprint, url_prefix='/settings')
 
     return app
diff --git a/web/app/auth/forms.py b/web/app/auth/forms.py
index e3f5c2766915db5f302aa634f8046d0fa29a1b4a..3344096b519da6a684170c2469f0a9bac09986d5 100644
--- a/web/app/auth/forms.py
+++ b/web/app/auth/forms.py
@@ -1,3 +1,4 @@
+from flask import current_app
 from ..models import User
 from flask_wtf import FlaskForm
 from wtforms import (BooleanField, PasswordField, StringField, SubmitField,
@@ -17,9 +18,9 @@ class RegistrationForm(FlaskForm):
     username = StringField(
         'Username',
         validators=[DataRequired(), Length(1, 64),
-                    Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
-                           'Usernames must have only letters, numbers, dots '
-                           'or underscores')]
+                    Regexp(current_app.config['ALLOWED_USERNAME_REGEX'],
+                           message='Usernames must have only letters, numbers,'
+                                   ' dots or underscores')]
     )
     password = PasswordField(
         'Password',
diff --git a/web/app/profile/forms.py b/web/app/profile/forms.py
deleted file mode 100644
index 14517e293ed0d48ba89e1d75fe43eb1b9ac4d6de..0000000000000000000000000000000000000000
--- a/web/app/profile/forms.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from flask_wtf import FlaskForm
-from wtforms import (BooleanField, PasswordField, SelectField, StringField,
-                     SubmitField, ValidationError)
-from wtforms.validators import DataRequired, Email, EqualTo
-
-
-class EditEmailForm(FlaskForm):
-    email = StringField('New email', validators=[Email(), DataRequired()])
-    save_email = SubmitField('Save email')
-
-
-class EditGeneralSettingsForm(FlaskForm):
-    dark_mode = BooleanField('Dark mode')
-    job_status_mail_notifications = SelectField(
-        'Job status mail notifications',
-        choices=[('', 'Choose your option'),
-                 ('all', 'Notify on all status changes'),
-                 ('end', 'Notify only when a job ended'),
-                 ('none', 'No status update notifications')],
-        validators=[DataRequired()])
-    job_status_site_notifications = SelectField(
-        'Job status site notifications',
-        choices=[('', 'Choose your option'),
-                 ('all', 'Notify on all status changes'),
-                 ('end', 'Notify only when a job ended'),
-                 ('none', 'No status update notifications')],
-        validators=[DataRequired()])
-    save_settings = SubmitField('Save settings')
-
-
-class EditPasswordForm(FlaskForm):
-    current_password = PasswordField('Current password',
-                                     validators=[DataRequired()])
-    password = PasswordField(
-        'New password',
-        validators=[DataRequired(), EqualTo('password_confirmation',
-                                            message='Passwords must match.')]
-    )
-    password_confirmation = PasswordField(
-        'Password confirmation',
-        validators=[DataRequired(),
-                    EqualTo('password', message='Passwords must match.')]
-    )
-    save_password = SubmitField('Save password')
-
-    def __init__(self, user, *args, **kwargs):
-        super(EditPasswordForm, self).__init__(*args, **kwargs)
-        self.user = user
-
-    def validate_current_password(self, field):
-        if not self.user.verify_password(field.data):
-            raise ValidationError('Invalid password.')
diff --git a/web/app/profile/tasks.py b/web/app/profile/tasks.py
deleted file mode 100644
index 61f737c564770c608860b136c79d8639dbe4defc..0000000000000000000000000000000000000000
--- a/web/app/profile/tasks.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from .. import db
-from ..decorators import background
-from ..models import User
-
-
-@background
-def delete_user(user_id, *args, **kwargs):
-    with kwargs['app'].app_context():
-        user = User.query.get(user_id)
-        if user is None:
-            raise Exception('User {} not found'.format(user_id))
-        user.delete()
-        db.session.commit()
diff --git a/web/app/profile/views.py b/web/app/profile/views.py
deleted file mode 100644
index f156715fbf824382c2d36a52a78482d12444f96c..0000000000000000000000000000000000000000
--- a/web/app/profile/views.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from flask import flash, redirect, render_template, url_for
-from flask_login import current_user, login_required, logout_user
-from . import profile
-from . import tasks
-from .forms import EditEmailForm, EditGeneralSettingsForm, EditPasswordForm
-from .. import db
-
-
-@profile.route('/settings', methods=['GET', 'POST'])
-@login_required
-def settings():
-    edit_email_form = EditEmailForm(prefix='edit-email-form')
-    edit_general_settings_form = EditGeneralSettingsForm(
-        prefix='edit-general-settings-form')
-    edit_password_form = EditPasswordForm(prefix='edit-password-form',
-                                          user=current_user)
-    # Check if edit_email_form is submitted and valid
-    if (edit_email_form.save_email.data
-            and edit_email_form.validate_on_submit()):
-        db.session.add(current_user)
-        db.session.commit()
-        flash('Your email address has been updated.')
-        return redirect(url_for('profile.settings'))
-    # Check if edit_settings_form is submitted and valid
-    if (edit_general_settings_form.save_settings.data
-            and edit_general_settings_form.validate_on_submit()):
-        current_user.setting_dark_mode = \
-            edit_general_settings_form.dark_mode.data
-        current_user.setting_job_status_mail_notifications = \
-            edit_general_settings_form.job_status_mail_notifications.data
-        current_user.setting_job_status_site_notifications = \
-            edit_general_settings_form.job_status_site_notifications.data
-        db.session.add(current_user)
-        db.session.commit()
-        flash('Your settings have been updated.')
-        return redirect(url_for('profile.settings'))
-    # Check if edit_password_form is submitted and valid
-    if (edit_password_form.save_password.data
-            and edit_password_form.validate_on_submit()):
-        current_user.password = edit_password_form.password.data
-        db.session.add(current_user)
-        db.session.commit()
-        flash('Your password has been updated.')
-        return redirect(url_for('profile.settings'))
-    # If no form is submitted or valid, fill out fields with current values
-    edit_email_form.email.data = current_user.email
-    edit_general_settings_form.dark_mode.data = current_user.setting_dark_mode
-    edit_general_settings_form.job_status_site_notifications.data = \
-        current_user.setting_job_status_site_notifications
-    edit_general_settings_form.job_status_mail_notifications.data = \
-        current_user.setting_job_status_mail_notifications
-    return render_template(
-        'profile/settings.html.j2',
-        edit_email_form=edit_email_form,
-        edit_password_form=edit_password_form,
-        edit_general_settings_form=edit_general_settings_form,
-        title='Settings')
-
-
-@profile.route('/delete', methods=['GET', 'POST'])
-@login_required
-def delete():
-    """
-    View to delete yourslef and all associated data.
-    """
-    tasks.delete_user(current_user.id)
-    logout_user()
-    flash('Your account has been deleted!')
-    return redirect(url_for('main.index'))
diff --git a/web/app/profile/__init__.py b/web/app/settings/__init__.py
similarity index 57%
rename from web/app/profile/__init__.py
rename to web/app/settings/__init__.py
index 85b9f61cdd705ef41af827f47c015aa5bd817917..2329d880513181a2642551fd3fd1076d8a727217 100644
--- a/web/app/profile/__init__.py
+++ b/web/app/settings/__init__.py
@@ -1,5 +1,5 @@
 from flask import Blueprint
 
 
-profile = Blueprint('profile', __name__)
+settings = Blueprint('settings', __name__)
 from . import views  # noqa
diff --git a/web/app/settings/forms.py b/web/app/settings/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..8cf9641501fc714c19416b62b027b4058bd0b69e
--- /dev/null
+++ b/web/app/settings/forms.py
@@ -0,0 +1,86 @@
+from flask import current_app
+from flask_login import current_user
+from flask_wtf import FlaskForm
+from wtforms import (BooleanField, PasswordField, SelectField, StringField,
+                     SubmitField, ValidationError)
+from wtforms.validators import DataRequired, Email, EqualTo, Length, Regexp
+
+
+class ChangePasswordForm(FlaskForm):
+    password = PasswordField('Old password', validators=[DataRequired()])
+    new_password = PasswordField(
+        'New password',
+        validators=[DataRequired(), EqualTo('password_confirmation',
+                                            message='Passwords must match.')]
+    )
+    new_password2 = PasswordField(
+        'Confirm new password', validators=[DataRequired()])
+    submit = SubmitField('Change password')
+
+    def __init__(self, user=current_user, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.user = user
+
+    def validate_current_password(self, field):
+        if not self.user.verify_password(field.data):
+            raise ValidationError('Invalid password.')
+
+
+class EditGeneralSettingsForm(FlaskForm):
+    dark_mode = BooleanField('Dark mode')
+    email = StringField('E-Mail',
+                        validators=[DataRequired(), Length(1, 254), Email()])
+    username = StringField(
+        'Benutzername',
+        validators=[DataRequired(),
+                    Length(1, 64),
+                    Regexp(current_app.config['ALLOWED_USERNAME_REGEX'],
+                           message='Usernames must have only letters, numbers,'
+                                   ' dots or underscores')]
+    )
+    submit = SubmitField('Submit')
+
+    def __init__(self, user=current_user, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.user = user
+        self.email.data = self.email.data or user.email
+        self.dark_mode.data = self.dark_mode.data or user.setting_dark_mode
+        self.username.data = self.username.data or user.username
+
+    def validate_email(self, field):
+        if (field.data != self.user.email
+                and User.query.filter_by(email=field.data).first()):
+            raise ValidationError('Email already registered.')
+
+    def validate_username(self, field):
+        if (field.data != self.user.username
+                and User.query.filter_by(username=field.data).first()):
+            raise ValidationError('Username already in use.')
+
+
+class EditNotificationSettingsForm(FlaskForm):
+    job_status_mail_notifications = SelectField(
+        'Job status mail notifications',
+        choices=[('', 'Choose your option'),
+                 ('all', 'Notify on all status changes'),
+                 ('end', 'Notify only when a job ended'),
+                 ('none', 'No status update notifications')],
+        validators=[DataRequired()])
+    job_status_site_notifications = SelectField(
+        'Job status site notifications',
+        choices=[('', 'Choose your option'),
+                 ('all', 'Notify on all status changes'),
+                 ('end', 'Notify only when a job ended'),
+                 ('none', 'No status update notifications')],
+        validators=[DataRequired()])
+    submit = SubmitField('Save settings')
+
+    def __init__(self, user=current_user, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.user = user
+        self.job_status_mail_notifications.data = \
+            self.job_status_mail_notifications.data \
+            or user.setting_job_status_mail_notifications
+        self.job_status_site_notifications.data = \
+            self.job_status_site_notifications.data \
+            or user.setting_job_status_site_notifications
diff --git a/web/app/settings/views.py b/web/app/settings/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..a90d8ab22293ce6798caf6904fd6fff9ff037a78
--- /dev/null
+++ b/web/app/settings/views.py
@@ -0,0 +1,73 @@
+from flask import current_app, flash, redirect, render_template, url_for
+from flask_login import current_user, login_required
+from . import settings
+from .forms import (ChangePasswordForm, EditGeneralSettingsForm,
+                    EditNotificationSettingsForm)
+from .. import db
+from ..decorators import admin_required
+from ..models import Role, User
+import os
+import uuid
+
+
+@settings.route('/')
+@login_required
+def index():
+    return redirect(url_for('.edit_general_settings'))
+
+
+@settings.route('/change_password', methods=['GET', 'POST'])
+@login_required
+def change_password():
+    form = ChangePasswordForm()
+    if form.validate_on_submit():
+        current_user.password = form.new_password.data
+        db.session.commit()
+        flash('Your password has been updated.')
+        return redirect(url_for('.change_password'))
+    return render_template('settings/change_password.html.j2',
+                           form=form,
+                           title='Change password')
+
+
+@settings.route('/edit_general_settings')
+@login_required
+def edit_general_settings():
+    form = EditGeneralSettingsForm()
+    if form.validate_on_submit():
+        current_user.email = form.email.data
+        current_user.setting_dark_mode = form.dark_mode.data
+        current_user.username = form.username.data
+        db.session.commit()
+        flash('Your changes have been saved.')
+    return render_template('settings/edit_general_settings.html.j2',
+                           form=form,
+                           title='General settings')
+
+
+@settings.route('/edit_notification_settings')
+@login_required
+def edit_notification_settings():
+    form = EditNotificationSettingsForm()
+    if form.validate_on_submit():
+        current_user.setting_job_status_mail_notifications = \
+            form.job_status_mail_notifications.data
+        current_user.setting_job_status_site_notifications = \
+            form.job_status_site_notifications.data
+        db.session.commit()
+        flash('Your changes have been saved.')
+    return render_template('settings/edit_notification_settings.html.j2',
+                           form=form,
+                           title='Notification settings')
+
+
+@settings.route('/delete')
+@login_required
+def delete():
+    """
+    View to delete current_user and all associated data.
+    """
+    tasks.delete_user(current_user.id)
+    logout_user()
+    flash('Your account has been deleted!')
+    return redirect(url_for('main.index'))
diff --git a/web/app/templates/nopaque.html.j2 b/web/app/templates/nopaque.html.j2
index c9097dd1cab72fc38625b46cd4aec53dc151107e..c5576a066ba665cfa6e3b63e9eba362ba2010527 100644
--- a/web/app/templates/nopaque.html.j2
+++ b/web/app/templates/nopaque.html.j2
@@ -84,8 +84,7 @@
           <li{% if request.path == url_for('auth.register') %} class="active"{% endif %}><a href="{{ url_for('auth.register') }}"><i class="material-icons left">assignment</i>Register</a></li>
           <li{% if request.path == url_for('auth.login') %} class="active"{% endif %}><a href="{{ url_for('auth.login') }}"><i class="material-icons left">login</i>Log in</a></li>
           {% else %}
-          <li{% if request.path == url_for('main.dashboard', _anchor='corpora') %} class="active"{% endif %}><a href="{{ url_for('main.dashboard', _anchor='corpora') }}"><i class="material-icons left">book</i>My Corpora</a></li>
-          <li{% if request.path == url_for('main.dashboard', _anchor='jobs') %} class="active"{% endif %}><a href="{{ url_for('main.dashboard', _anchor='jobs') }}"><i class="material-icons left">work</i>My Jobs</a></li>
+          <li{% if request.path == url_for('main.dashboard') %} class="active"{% endif %}><a href="{{ url_for('main.dashboard') }}"><i class="material-icons left">dashboard</i>Dashboard</a></li>
           <li><a class="dropdown-trigger no-autoinit" data-target="nav-more-dropdown" href="#!" id="nav-more-dropdown-trigger"><i class="material-icons">more_vert</i></a></li>
           {% endif %}
         </ul>
@@ -98,7 +97,7 @@
 
   {% if current_user.is_authenticated %}
   <ul class="dropdown-content" id="nav-more-dropdown">
-    <li><a href="{{ url_for('profile.settings') }}"><i class="material-icons left">settings</i>Settings</a></li>
+    <li><a href="{{ url_for('settings.index') }}"><i class="material-icons left">settings</i>Settings</a></li>
     <li class="divider" tabindex="-1"></li>
     <li><a href="{{ url_for('auth.logout') }}">Log out</a></li>
   </ul>
@@ -127,7 +126,7 @@
   <li style="background-color: {{ colors.nlp }}; border-left: 10px solid {{ colors.nlp_darken }}; margin-top: 5px;"><a href="{{ url_for('services.service', service='nlp') }}"><i class="material-icons">format_textdirection_l_to_r</i>NLP</a></li>
   <li style="background-color: {{ colors.corpus_analysis }}; border-left: 10px solid {{ colors.corpus_analysis_darken }}; margin-top: 5px;"><a href="{{ url_for('services.service', service='corpus_analysis') }}"><i class="material-icons">search</i>Corpus analysis</a></li>
   <li class="hide-on-large-only"><div class="divider"></div></li>
-  <li class="hide-on-large-only"><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
+  <li class="hide-on-large-only"><a href="{{ url_for('settings.index') }}"><i class="material-icons">settings</i>Settings</a></li>
   <li class="hide-on-large-only"><a href="{{ url_for('auth.logout') }}">Log out</a></li>
   {% else %}
   <li class="hide-on-large-only"><a href="{{ url_for('auth.register') }}"><i class="material-icons">assignment</i>Register</a></li>
diff --git a/web/app/templates/profile/settings.html.j2 b/web/app/templates/profile/settings.html.j2
deleted file mode 100644
index 64e5152b3e729b7af10056fb4d567d809cfe430e..0000000000000000000000000000000000000000
--- a/web/app/templates/profile/settings.html.j2
+++ /dev/null
@@ -1,136 +0,0 @@
-{% extends "nopaque.html.j2" %}
-
-{% block page_content %}
-<div class="col s12 m4">
-  <h3>General settings</h3>
-</div>
-<div class="col s12 m8">
-  <br class="hide-on-small-only">
-  <div class="card">
-    <form method="POST">
-      <div class="card-content">
-        {{ edit_general_settings_form.hidden_tag() }}
-        <div class="row">
-          <div class="col s9">
-            <p><i class="material-icons left">brightness_3</i>{{ edit_general_settings_form.dark_mode.label.text }}</p>
-            <p class="light">Activate dark mode to ease your eyes.</p>
-          </div>
-          <div class="col s3 right-align">
-            {{ M.render_field(edit_general_settings_form.dark_mode, label=False) }}
-          </div>
-          <div class="col s12"><p>&nbsp;</p></div>
-          <div class="col s12 divider"></div>
-          <div class="col s12"><p>&nbsp;</p></div>
-          <div class="col s12 m8">
-            <p><i class="material-icons left">notifications</i>Job status site notifications</p>
-            <p class="light">Receive site notifications about job status changes.</p>
-          </div>
-          <div class="col s12 m4 right-align" style="margin-top: -1rem;">
-            {{ M.render_field(edit_general_settings_form.job_status_site_notifications, label=False) }}
-          </div>
-          <div class="col s12"><p>&nbsp;</p></div>
-          <div class="col s12 divider"></div>
-          <div class="col s12"><p>&nbsp;</p></div>
-          <div class="col s12 m8">
-            <p><i class="material-icons left">notifications</i>Job status mail notifications</p>
-            <p class="light">Receive mail notifications about job status changes.</p>
-          </div>
-          <div class="col s12 m4 right-align" style="margin-top: -1rem;">
-            {{ M.render_field(edit_general_settings_form.job_status_mail_notifications, label=False) }}
-          </div>
-          <!--
-          Seperate each setting with the following two elements
-          <div class="col s12 divider"></div>
-          <div class="col s12"><p>&nbsp;</p></div>
-          -->
-        </div>
-      </div>
-      <div class="card-action right-align">
-        {{ M.render_field(edit_general_settings_form.save_settings, material_icon='send') }}
-      </div>
-    </form>
-  </div>
-</div>
-
-
-<div class="col s12"></div>
-
-
-<div class="col s12 m4">
-  <h3>Change password</h3>
-</div>
-<div class="col s12 m8">
-  <br class="hide-on-small-only">
-  <div class="card">
-    <form method="POST">
-      <div class="card-content">
-        {{ edit_password_form.hidden_tag() }}
-        {{ M.render_field(edit_password_form.current_password, data_length='128', material_icon='vpn_key') }}
-        {{ M.render_field(edit_password_form.password, data_length='128', material_icon='vpn_key') }}
-        {{ M.render_field(edit_password_form.password_confirmation, data_length='128', material_icon='vpn_key') }}
-      </div>
-      <div class="card-action right-align">
-        {{ M.render_field(edit_password_form.save_password, material_icon='send') }}
-      </div>
-    </form>
-  </div>
-</div>
-
-
-<div class="col s12"></div>
-
-
-<div class="col s12 m4">
-  <h3>Change email</h3>
-</div>
-<div class="col s12 m8">
-  <br class="hide-on-small-only">
-  <div class="card">
-    <form method="POST">
-      <div class="card-content">
-        {{ edit_email_form.hidden_tag() }}
-        {{ M.render_field(edit_email_form.email, class_='validate', material_icon='email', type='email') }}
-      </div>
-      <div class="card-action right-align">
-        {{ M.render_field(edit_email_form.save_email, material_icon='send') }}
-      </div>
-    </form>
-  </div>
-</div>
-
-
-<div class="col s12"></div>
-
-
-<div class="col s12 m4">
-  <h3>Delete account</h3>
-</div>
-<div class="col s12 m8">
-  <br class="hide-on-small-only">
-  <div class="card">
-    <div class="card-content">
-      <p>Deleting an account has the following effects:</p>
-      <ul>
-        <li>All data associated with your corpora and jobs will be permanently deleted.</li>
-        <li>All settings will be permanently deleted.</li>
-      </ul>
-    </div>
-    <div class="card-action right-align">
-      <a href="#delete-account-modal" class="btn modal-trigger red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
-    </div>
-  </div>
-</div>
-
-
-<!-- Modals -->
-<div class="modal" id="delete-account-modal">
-  <div class="modal-content">
-    <h4>Confirm deletion</h4>
-    <p>Do you really want to delete your account and all associated data? All associated corpora, jobs and files will be permanently deleted!</p>
-  </div>
-  <div class="modal-footer">
-    <a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
-    <a href="{{ url_for('profile.delete') }}" class="btn red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
-  </div>
-</div>
-{% endblock %}
diff --git a/web/app/templates/settings/_menu.html.j2 b/web/app/templates/settings/_menu.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..554a3f67eb49b1bcaabffda54ada154afa959bad
--- /dev/null
+++ b/web/app/templates/settings/_menu.html.j2
@@ -0,0 +1,5 @@
+<div class="collection">
+  <a href="{{ url_for('.edit_general_settings') }}" class="collection-item{%if request.path == url_for('.edit_general_settings') %} active{% endif %}">Edit general settings</a>
+  <a href="{{ url_for('.change_password') }}" class="collection-item{%if request.path == url_for('.change_password') %} active{% endif %}">Change password</a>
+  <a href="{{ url_for('.edit_notification_settings') }}" class="collection-item{%if request.path == url_for('.edit_notification_settings') %} active{% endif %}">Edit notification settings</a>
+</div>
diff --git a/web/app/templates/settings/change_password.html.j2 b/web/app/templates/settings/change_password.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..771886bf7d8f17fd7d0b0e39ac3ccb42704e25fe
--- /dev/null
+++ b/web/app/templates/settings/change_password.html.j2
@@ -0,0 +1,35 @@
+{% extends 'nopaque.html.j2' %}
+{% import 'materialize/wtf.html.j2' as wtf %}
+
+{% block page_content %}
+<div class="container">
+  <div class="row">
+    <div class="col s12">
+      <h1 id="title">Settings</h1>
+    </div>
+
+    <div class="col s12 m4">
+      {% include 'settings/_menu.html.j2' %}
+    </div>
+
+    <div class="col s12 m8">
+      <div class="card">
+        <form enctype="multipart/form-data" method="POST">
+          <div class="card-content">
+            <span class="card-title">{{ title }}</span>
+            {{ form.hidden_tag() }}
+            {{ wtf.render_field(form.password, material_icon='vpn_key') }}
+            {{ wtf.render_field(form.new_password, material_icon='vpn_key') }}
+            {{ wtf.render_field(form.new_password2, material_icon='vpn_key') }}
+          </div>
+          <div class="card-action">
+            <div class="right-align">
+              {{ wtf.render_field(form.submit, material_icon='send') }}
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock page_content %}
diff --git a/web/app/templates/settings/edit_general_settings.html.j2 b/web/app/templates/settings/edit_general_settings.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..4126ee8eb4b289bcbcb2494d29855faed0386361
--- /dev/null
+++ b/web/app/templates/settings/edit_general_settings.html.j2
@@ -0,0 +1,63 @@
+{% extends 'nopaque.html.j2' %}
+{% import 'materialize/wtf.html.j2' as wtf %}
+
+{% block page_content %}
+<div class="container">
+  <div class="row">
+    <div class="col s12">
+      <h1 id="title">Settings</h1>
+    </div>
+
+    <div class="col s12 m4">
+      {% include 'settings/_menu.html.j2' %}
+    </div>
+
+    <div class="col s12 m8">
+      <div class="card">
+        <form enctype="multipart/form-data" method="POST">
+          <div class="card-content">
+            <span class="card-title">{{ title }}</span>
+            {{ form.hidden_tag() }}
+            {{ wtf.render_field(form.username, data_length='64', material_icon='person') }}
+            {{ wtf.render_field(form.email, data_length='254', material_icon='email') }}
+            {{ wtf.render_field(form.dark_mode, material_icon='brightness_3') }}
+          </div>
+          <div class="card-action">
+            <div class="right-align">
+
+              {{ wtf.render_field(form.submit, material_icon='send') }}
+            </div>
+          </div>
+        </form>
+      </div>
+
+      <div class="card">
+        <div class="card-content">
+          <span class="card-title">Delete account</span>
+          <p>Deleting an account has the following effects:</p>
+          <ul>
+            <li>All data associated with your corpora and jobs will be permanently deleted.</li>
+            <li>All settings will be permanently deleted.</li>
+          </ul>
+        </div>
+        <div class="card-action right-align">
+          <a href="#delete-account-modal" class="btn modal-trigger red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+
+<!-- Modals -->
+<div class="modal" id="delete-account-modal">
+  <div class="modal-content">
+    <h4>Confirm deletion</h4>
+    <p>Do you really want to delete your account and all associated data? All associated corpora, jobs and files will be permanently deleted!</p>
+  </div>
+  <div class="modal-footer">
+    <a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
+    <a href="{{ url_for('.delete') }}" class="btn red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
+  </div>
+</div>
+{% endblock page_content %}
diff --git a/web/app/templates/settings/edit_notification_settings.html.j2 b/web/app/templates/settings/edit_notification_settings.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2a8e7b24f02bcedb87a784fe21e5a6f70cb70cfa
--- /dev/null
+++ b/web/app/templates/settings/edit_notification_settings.html.j2
@@ -0,0 +1,34 @@
+{% extends 'nopaque.html.j2' %}
+{% import 'materialize/wtf.html.j2' as wtf %}
+
+{% block page_content %}
+<div class="container">
+  <div class="row">
+    <div class="col s12">
+      <h1 id="title">Settings</h1>
+    </div>
+
+    <div class="col s12 m4">
+      {% include 'settings/_menu.html.j2' %}
+    </div>
+
+    <div class="col s12 m8">
+      <div class="card">
+        <form enctype="multipart/form-data" method="POST">
+          <div class="card-content">
+            <span class="card-title">{{ title }}</span>
+            {{ form.hidden_tag() }}
+            {{ wtf.render_field(form.job_status_mail_notifications, material_icon='notifications') }}
+            {{ wtf.render_field(form.job_status_site_notifications, material_icon='feedback') }}
+          </div>
+          <div class="card-action">
+            <div class="right-align">
+              {{ wtf.render_field(form.submit, material_icon='send') }}
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock page_content %}
diff --git a/web/config.py b/web/config.py
index 0f78e6adf6d7642a1eec3a4561f20f5032a950cc..4ca3704fa524c804bc26e5f172945703fe8fbbe0 100644
--- a/web/config.py
+++ b/web/config.py
@@ -31,6 +31,7 @@ class Config:
 
     ''' # General # '''
     ADMIN_EMAIL_ADRESS = os.environ.get('NOPAQUE_ADMIN_EMAIL_ADRESS')
+    ALLOWED_USERNAME_REGEX = '^[A-Za-zÄÖÜäöüß0-9_.]*$'
     CONTACT_EMAIL_ADRESS = os.environ.get('NOPAQUE_CONTACT_EMAIL_ADRESS')
     DATA_DIR = os.environ.get('NOPAQUE_DATA_DIR', '/mnt/nopaque')
     SECRET_KEY = os.environ.get('NOPAQUE_SECRET_KEY', 'hard to guess string')