diff --git a/LavalinkServer/build.gradle.kts b/LavalinkServer/build.gradle.kts
index ddb62b35a..2e7b623b8 100644
--- a/LavalinkServer/build.gradle.kts
+++ b/LavalinkServer/build.gradle.kts
@@ -8,6 +8,7 @@ plugins {
     application
     kotlin("jvm")
     id("org.jetbrains.dokka")
+    kotlin("plugin.serialization")
     alias(libs.plugins.maven.publish.base)
 }
 
@@ -30,6 +31,15 @@ java {
     sourceCompatibility = JavaVersion.VERSION_17
 }
 
+kotlin {
+    sourceSets {
+        all {
+            languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
+            languageSettings.optIn("nl.adaptivity.xmlutil.ExperimentalXmlUtilApi")
+        }
+    }
+}
+
 configurations {
     compileOnly {
         extendsFrom(annotationProcessor.get())
@@ -42,6 +52,9 @@ dependencies {
         exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
     }
 
+    implementation(libs.xmlutil.jdk)
+    implementation(libs.xmlutil.serialization)
+
     implementation(libs.bundles.metrics)
     implementation(libs.bundles.spring) {
         exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
diff --git a/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt b/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt
index c3ed4cb73..1ca92028a 100644
--- a/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt
+++ b/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt
@@ -1,5 +1,8 @@
 package lavalink.server.bootstrap
 
+import dev.arbjerg.lavalink.protocol.v4.Version
+import nl.adaptivity.xmlutil.core.KtXmlReader
+import nl.adaptivity.xmlutil.serialization.XML
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.springframework.boot.autoconfigure.SpringBootApplication
@@ -7,6 +10,7 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver
 import java.io.File
 import java.io.FileOutputStream
 import java.io.InputStream
+import java.net.HttpURLConnection
 import java.net.URL
 import java.net.URLClassLoader
 import java.nio.channels.Channels
@@ -44,7 +48,7 @@ class PluginManager(val config: PluginsConfig) {
                     loadPluginManifests(jar).map { manifest -> PluginJar(manifest, file) }
                 }
             }
-            ?.onEach { log.info("Found plugin '${it.manifest.name}' version ${it.manifest.version}") }
+            ?.onEach { log.info("Found plugin '${it.manifest.name}' version '${it.manifest.version}'") }
             ?: return
 
         val declarations = config.plugins.map { declaration ->
@@ -83,6 +87,44 @@ class PluginManager(val config: PluginsConfig) {
                 val file = File(directory, declaration.canonicalJarName)
                 downloadJar(file, url)
             }
+
+            checkPluginForUpdates(declaration)
+        }
+    }
+
+    private fun checkPluginForUpdates(declaration: Declaration) {
+        val baseSplitPath = declaration.url.split('/').dropLast(2)
+        val metadataUrl = baseSplitPath.joinToString("/") + "/maven-metadata.xml"
+
+        val url = URL(metadataUrl)
+        val conn = url.openConnection() as HttpURLConnection
+
+        if (conn.responseCode != HttpURLConnection.HTTP_OK) {
+            log.warn("Failed to check for updates for ${declaration.name}: ${conn.responseMessage}")
+            return
+        }
+
+        val metadata: Metadata
+        conn.inputStream.use {
+            metadata = XML.decodeFromReader(Metadata.serializer(), KtXmlReader(it))
+        }
+
+        val current = Version.fromSemver(declaration.version)
+        var latest = metadata.versioning.latest
+        if (latest.isEmpty()) {
+            latest = metadata.versioning.release
+        }
+        if (latest.isEmpty()) {
+            latest = (metadata.versioning.versions.lastOrNull() ?: "").toString()
+        }
+
+        if (latest.isEmpty()) {
+            return
+        }
+
+        val latestVersion = Version.fromSemver(latest)
+        if (latestVersion > current) {
+            log.warn("A newer version of '${declaration.name}' was found: '$latestVersion', The current version is '$current' please update the version in your configuration.")
         }
     }
 
@@ -98,7 +140,7 @@ class PluginManager(val config: PluginsConfig) {
         return PathMatchingResourcePatternResolver()
             .getResources("classpath*:lavalink-plugins/*.properties")
             .map { parsePluginManifest(it.inputStream) }
-            .onEach { log.info("Found plugin '${it.name}' version ${it.version}") }
+            .onEach { log.info("Found plugin '${it.name}' version '${it.version}'") }
     }
 
     private fun loadJars(): List<PluginManifest> {
@@ -132,14 +174,15 @@ class PluginManager(val config: PluginsConfig) {
             for (entry in it.entries()) {
                 if (entry.isDirectory ||
                     !entry.name.endsWith(".class") ||
-                    allowedPaths.none(entry.name::startsWith)) continue
+                    allowedPaths.none(entry.name::startsWith)
+                ) continue
 
                 cl.loadClass(entry.name.dropLast(6).replace("/", "."))
                 classCount++
             }
         }
 
-        log.info("Loaded ${file.name} ($classCount classes)")
+        log.info("Loaded '${file.name}' ($classCount classes)")
         return manifests
     }
 
diff --git a/LavalinkServer/src/main/java/lavalink/server/bootstrap/maven.kt b/LavalinkServer/src/main/java/lavalink/server/bootstrap/maven.kt
new file mode 100644
index 000000000..ec3d798d5
--- /dev/null
+++ b/LavalinkServer/src/main/java/lavalink/server/bootstrap/maven.kt
@@ -0,0 +1,31 @@
+package lavalink.server.bootstrap
+
+import kotlinx.serialization.Serializable
+import nl.adaptivity.xmlutil.serialization.XmlChildrenName
+import nl.adaptivity.xmlutil.serialization.XmlElement
+import nl.adaptivity.xmlutil.serialization.XmlSerialName
+
+@Serializable
+@XmlSerialName("metadata")
+data class Metadata(
+    @XmlElement(true)
+    val groupId: String,
+    @XmlElement(true)
+    val artifactId: String,
+    @XmlElement(true)
+    val versioning: Versioning
+)
+
+@Serializable
+@XmlSerialName("versioning")
+data class Versioning(
+    @XmlElement(true)
+    val latest: String,
+    @XmlElement(true)
+    val release: String,
+    @XmlElement(true)
+    @XmlChildrenName("version")
+    val versions: List<String>,
+    @XmlElement(true)
+    val lastUpdated: String
+)
diff --git a/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt b/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt
index d739c53e3..2040c25cc 100644
--- a/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt
+++ b/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt
@@ -62,6 +62,70 @@ data class Version(
             return Version(semver, major.toInt(), minor.toInt(), patch.toInt(), preRelease)
         }
     }
+
+    override fun toString(): String {
+        var baseSemver = "${major}.${minor}.${patch}"
+
+        if(!preRelease.isNullOrEmpty())
+            baseSemver += "-${preRelease}"
+
+        return baseSemver
+    }
+
+    operator fun compareTo(other: Version): Int {
+        // Compare major, minor, and patch
+        val majorDiff = major - other.major
+        if (majorDiff != 0) return majorDiff
+
+        val minorDiff = minor - other.minor
+        if (minorDiff != 0) return minorDiff
+
+        val patchDiff = patch - other.patch
+        if (patchDiff != 0) return patchDiff
+
+        // Compare prerelease (null means no prerelease and is greater)
+        return when {
+            preRelease.isNullOrEmpty() && other.preRelease.isNullOrEmpty() -> 0
+            preRelease.isNullOrEmpty() && !other.preRelease.isNullOrEmpty() -> 1
+            !preRelease.isNullOrEmpty() && other.preRelease.isNullOrEmpty() -> -1
+            !preRelease.isNullOrEmpty() && !other.preRelease.isNullOrEmpty() -> comparePreRelease(preRelease, other.preRelease)
+            else -> 0
+        }
+    }
+
+    private fun comparePreRelease(part1: String, part2: String): Int {
+        val components1 = part1.split(".")
+        val components2 = part2.split(".")
+        val maxLength = maxOf(components1.size, components2.size)
+
+        for (i in 0 until maxLength) {
+            val comp1 = components1.getOrNull(i)
+            val comp2 = components2.getOrNull(i)
+
+            if (comp1 == null) return -1 // `part1` is shorter and considered smaller
+            if (comp2 == null) return 1  // `part2` is shorter and considered smaller
+
+            val isNumeric1 = comp1.all { it.isDigit() }
+            val isNumeric2 = comp2.all { it.isDigit() }
+
+            when {
+                isNumeric1 && isNumeric2 -> {
+                    // Compare numerically
+                    val diff = comp1.toInt() - comp2.toInt()
+                    if (diff != 0) return diff
+                }
+                isNumeric1 -> return -1 // Numeric parts come before string parts
+                isNumeric2 -> return 1  // String parts come after numeric parts
+                else -> {
+                    // Compare lexicographically
+                    val diff = comp1.compareTo(comp2)
+                    if (diff != 0) return diff
+                }
+            }
+        }
+
+        return 0 // Parts are equal
+    }
 }
 
 @Serializable
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 14cbf4aa8..0a09abe54 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -73,6 +73,9 @@ fun VersionCatalogBuilder.common() {
     library("kotlinx-serialization-json", "org.jetbrains.kotlinx", "kotlinx-serialization-json").version("1.7.0")
     library("kotlinx-datetime", "org.jetbrains.kotlinx", "kotlinx-datetime").version("0.6.0")
 
+    library("xmlutil-jdk", "io.github.pdvrieze.xmlutil", "core-jdk").version("0.90.3")
+    library("xmlutil-serialization", "io.github.pdvrieze.xmlutil", "serialization-jvm").version("0.90.3")
+
     library("logback",        "ch.qos.logback",       "logback-classic").version("1.5.6")
     library("sentry-logback", "io.sentry",            "sentry-logback").version("7.10.0")
     library("oshi",           "com.github.oshi",      "oshi-core").version("6.4.11")