diff --git a/set_version.sh b/set_version.sh
index 990e58aa2bda000829e6f119d8600fecf49fcc4b..8121b54113ff19d3c4ed825848f4d4063a435ada 100755
--- a/set_version.sh
+++ b/set_version.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
-export version=0.5.1-alpha.7
+export version=0.5.1-alpha.8
 echo "pub const VERSION: &str = \"${version}\";" > src/version.rs
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 4490d9047ff6bcabcd0f8ed14716a9c80ddb0230..aa28cd76d55460000cd5b099371659a87dfbc89a 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -46,6 +46,11 @@ pub enum RemoveServerField {
     Description,
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub enum RemoveRoleField {
+    Colour,
+}
+
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum RemoveMemberField {
     Nickname,
@@ -132,7 +137,9 @@ pub enum ClientboundNotification {
     ServerRoleUpdate {
         id: String,
         role_id: String,
-        data: JsonValue
+        data: JsonValue,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        clear: Option<RemoveRoleField>
     },
     ServerRoleDelete {
         id: String,
diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs
index 15968de8f3f6d32df4ae9db6d854199f4a151924..b137d26471afb619078cc071e98591285424ed1b 100644
--- a/src/routes/servers/mod.rs
+++ b/src/routes/servers/mod.rs
@@ -19,6 +19,7 @@ mod ban_remove;
 mod invites_fetch;
 
 mod roles_create;
+mod roles_edit;
 mod roles_delete;
 mod permissions_set;
 mod permissions_set_default;
@@ -39,6 +40,7 @@ pub fn routes() -> Vec<Route> {
         ban_list::req,
         invites_fetch::req,
         roles_create::req,
+        roles_edit::req,
         roles_delete::req,
         permissions_set::req,
         permissions_set_default::req
diff --git a/src/routes/servers/permissions_set.rs b/src/routes/servers/permissions_set.rs
index 62ffa3c8583a01f357a16c18d3f9057e33c7f6e9..74359a24e2b85f1f681b7cb50a5aaee96fb3ef61 100644
--- a/src/routes/servers/permissions_set.rs
+++ b/src/routes/servers/permissions_set.rs
@@ -66,7 +66,8 @@ pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) ->
                 server_permissions as i32,
                 channel_permissions as i32
             ]
-        })
+        }),
+        clear: None
     }
     .publish(target.id);
 
diff --git a/src/routes/servers/roles_create.rs b/src/routes/servers/roles_create.rs
index e55887c2e8c2b700845e2c8fa988d328ed9198f8..3f13d753d740fc5e754f7437fb8b9887d09e3e47 100644
--- a/src/routes/servers/roles_create.rs
+++ b/src/routes/servers/roles_create.rs
@@ -67,7 +67,8 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<JsonValue>
         data: json!({
             "name": data.name,
             "permissions": &perm_tuple
-        })
+        }),
+        clear: None
     }
     .publish(target.id);
 
diff --git a/src/routes/servers/roles_edit.rs b/src/routes/servers/roles_edit.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3cc796e2481ee6c5cee69fe7da686d44ed23296a
--- /dev/null
+++ b/src/routes/servers/roles_edit.rs
@@ -0,0 +1,98 @@
+use crate::notifications::events::ClientboundNotification;
+use crate::util::result::{Error, Result};
+use crate::{database::*, notifications::events::RemoveRoleField};
+
+use mongodb::bson::doc;
+use rocket_contrib::json::Json;
+use serde::{Deserialize, Serialize};
+use validator::Validate;
+
+#[derive(Validate, Serialize, Deserialize)]
+pub struct Data {
+    #[validate(length(min = 1, max = 32))]
+    name: Option<String>,
+    #[validate(length(min = 1, max = 32))]
+    colour: Option<String>,
+    remove: Option<RemoveRoleField>,
+}
+
+#[patch("/<target>/roles/<role_id>", data = "<data>")]
+pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) -> Result<()> {
+    let data = data.into_inner();
+    data.validate()
+        .map_err(|error| Error::FailedValidation { error })?;
+
+    if data.name.is_none() && data.colour.is_none() && data.remove.is_none()
+    {
+        return Ok(());
+    }
+
+    let target = target.fetch_server().await?;
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
+        .await?;
+
+    if !perm.get_manage_roles() {
+        return Err(Error::MissingPermission)
+    }
+
+    if !target.roles.contains_key(&role_id) {
+        return Err(Error::InvalidRole)
+    }
+
+    let mut set = doc! {};
+    let mut unset = doc! {};
+
+    // ! FIXME: we should probably just require clients to support basic MQL incl. $set / $unset
+    let mut set_update = doc! {};
+
+    let role_key = "roles.".to_owned() + &role_id;
+
+    if let Some(remove) = &data.remove {
+        match remove {
+            RemoveRoleField::Colour => {
+                unset.insert(role_key.clone() + ".colour", 1);
+            }
+        }
+    }
+
+    if let Some(name) = &data.name {
+        set.insert(role_key.clone() + ".name", name);
+        set_update.insert("name", name);
+    }
+
+    if let Some(colour) = &data.colour {
+        set.insert(role_key.clone() + ".colour", colour);
+        set_update.insert("colour", colour);
+    }
+
+    let mut operations = doc! {};
+    if set.len() > 0 {
+        operations.insert("$set", &set);
+    }
+
+    if unset.len() > 0 {
+        operations.insert("$unset", unset);
+    }
+
+    if operations.len() > 0 {
+        get_collection("servers")
+            .update_one(doc! { "_id": &target.id }, operations, None)
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "update_one",
+                with: "server",
+            })?;
+    }
+
+    ClientboundNotification::ServerRoleUpdate {
+        id: target.id.clone(),
+        role_id,
+        data: json!(set_update),
+        clear: data.remove,
+    }
+    .publish(target.id.clone());
+
+    Ok(())
+}
diff --git a/src/version.rs b/src/version.rs
index 99ae5c62b0d2606e370a8fffe0cdf5dbf7b92c70..918a989a77ebf875569ab16010e24e4c483e7fc1 100644
--- a/src/version.rs
+++ b/src/version.rs
@@ -1 +1 @@
-pub const VERSION: &str = "0.5.1-alpha.7";
+pub const VERSION: &str = "0.5.1-alpha.8";