diff --git a/Agent/Phantom.Agent.Minecraft/Server/ServerStatusProtocol.cs b/Agent/Phantom.Agent.Minecraft/Server/ServerStatusProtocol.cs
index 947b399..75b4b31 100644
--- a/Agent/Phantom.Agent.Minecraft/Server/ServerStatusProtocol.cs
+++ b/Agent/Phantom.Agent.Minecraft/Server/ServerStatusProtocol.cs
@@ -3,13 +3,12 @@ using System.Buffers.Binary;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
+using Phantom.Common.Data.Instance;
 
 namespace Phantom.Agent.Minecraft.Server;
 
 public static class ServerStatusProtocol {
-	public readonly record struct PlayerCounts(int Online, int Maximum);
-	
-	public static async Task<PlayerCounts> GetPlayerCounts(ushort serverPort, CancellationToken cancellationToken) {
+	public static async Task<InstancePlayerCounts> GetPlayerCounts(ushort serverPort, CancellationToken cancellationToken) {
 		using var tcpClient = new TcpClient();
 		await tcpClient.ConnectAsync(IPAddress.Loopback, serverPort, cancellationToken);
 		var tcpStream = tcpClient.GetStream();
@@ -42,7 +41,7 @@ public static class ServerStatusProtocol {
 		}
 	}
 
-	private static async Task<PlayerCounts> ReadPlayerCounts(NetworkStream tcpStream, int messageLength, CancellationToken cancellationToken) {
+	private static async Task<InstancePlayerCounts> ReadPlayerCounts(NetworkStream tcpStream, int messageLength, CancellationToken cancellationToken) {
 		var messageBuffer = ArrayPool<byte>.Shared.Rent(messageLength);
 		try {
 			await tcpStream.ReadExactlyAsync(messageBuffer, 0, messageLength, cancellationToken);
@@ -57,7 +56,7 @@ public static class ServerStatusProtocol {
 	/// </summary>
 	private static readonly byte[] Separator = { 0x00, 0xA7 };
 	
-	private static PlayerCounts ReadPlayerCountsFromResponse(ReadOnlySpan<byte> messageBuffer) {
+	private static InstancePlayerCounts ReadPlayerCountsFromResponse(ReadOnlySpan<byte> messageBuffer) {
 		int lastSeparator = messageBuffer.LastIndexOf(Separator);
 		int middleSeparator = messageBuffer[..lastSeparator].LastIndexOf(Separator);
 		
@@ -71,7 +70,7 @@ public static class ServerStatusProtocol {
 		// Player counts are integers, whose maximum string length is 10 characters.
 		Span<char> integerStringBuffer = stackalloc char[10];
 		
-		return new PlayerCounts(
+		return new InstancePlayerCounts(
 			DecodeAndParsePlayerCount(onlinePlayerCountBuffer, integerStringBuffer, "online"),
 			DecodeAndParsePlayerCount(maximumPlayerCountBuffer, integerStringBuffer, "maximum")
 		);
diff --git a/Agent/Phantom.Agent.Services/Instances/State/InstancePlayerCountTracker.cs b/Agent/Phantom.Agent.Services/Instances/State/InstancePlayerCountTracker.cs
index e7289b6..22c1357 100644
--- a/Agent/Phantom.Agent.Services/Instances/State/InstancePlayerCountTracker.cs
+++ b/Agent/Phantom.Agent.Services/Instances/State/InstancePlayerCountTracker.cs
@@ -1,5 +1,8 @@
 using Phantom.Agent.Minecraft.Instance;
 using Phantom.Agent.Minecraft.Server;
+using Phantom.Agent.Rpc;
+using Phantom.Common.Data.Instance;
+using Phantom.Common.Messages.Agent.ToController;
 using Phantom.Utils.Logging;
 using Phantom.Utils.Tasks;
 using Phantom.Utils.Threading;
@@ -7,32 +10,35 @@ using Phantom.Utils.Threading;
 namespace Phantom.Agent.Services.Instances.State;
 
 sealed class InstancePlayerCountTracker : CancellableBackgroundTask {
-	private readonly InstanceProcess process;
+	private readonly ControllerConnection controllerConnection;
+	private readonly Guid instanceGuid;
 	private readonly ushort serverPort;
+	private readonly InstanceProcess process;
 
 	private readonly TaskCompletionSource firstDetection = AsyncTasks.CreateCompletionSource();
 	private readonly ManualResetEventSlim serverOutputEvent = new ();
 
-	private int? onlinePlayerCount;
+	private InstancePlayerCounts? playerCounts;
 
-	public int? OnlinePlayerCount {
+	public InstancePlayerCounts? PlayerCounts {
 		get {
 			lock (this) {
-				return onlinePlayerCount;
+				return playerCounts;
 			}
 		}
 		private set {
 			EventHandler<int?>? onlinePlayerCountChanged;
 			lock (this) {
-				if (onlinePlayerCount == value) {
+				if (playerCounts == value) {
 					return;
 				}
 				
-				onlinePlayerCount = value;
+				playerCounts = value;
 				onlinePlayerCountChanged = OnlinePlayerCountChanged;
 			}
 
-			onlinePlayerCountChanged?.Invoke(this, value);
+			onlinePlayerCountChanged?.Invoke(this, value?.Online);
+			controllerConnection.Send(new ReportInstancePlayerCountsMessage(instanceGuid, value));
 		}
 	}
 
@@ -41,6 +47,8 @@ sealed class InstancePlayerCountTracker : CancellableBackgroundTask {
 	private bool isDisposed = false;
 
 	public InstancePlayerCountTracker(InstanceContext context, InstanceProcess process, ushort serverPort) : base(PhantomLogger.Create<InstancePlayerCountTracker>(context.ShortName)) {
+		this.controllerConnection = context.Services.ControllerConnection;
+		this.instanceGuid = context.InstanceGuid;
 		this.process = process;
 		this.serverPort = serverPort;
 		Start();
@@ -56,7 +64,7 @@ sealed class InstancePlayerCountTracker : CancellableBackgroundTask {
 		while (!CancellationToken.IsCancellationRequested) {
 			serverOutputEvent.Reset();
 
-			OnlinePlayerCount = await TryGetOnlinePlayerCount();
+			PlayerCounts = await TryGetPlayerCounts();
 			
 			if (!firstDetection.Task.IsCompleted) {
 				firstDetection.SetResult();
@@ -68,11 +76,11 @@ sealed class InstancePlayerCountTracker : CancellableBackgroundTask {
 		}
 	}
 
-	private async Task<int?> TryGetOnlinePlayerCount() {
+	private async Task<InstancePlayerCounts?> TryGetPlayerCounts() {
 		try {
-			var (online, maximum) = await ServerStatusProtocol.GetPlayerCounts(serverPort, CancellationToken);
-			Logger.Debug("Detected {OnlinePlayerCount} / {MaximumPlayerCount} online player(s).", online, maximum);
-			return online;
+			var result = await ServerStatusProtocol.GetPlayerCounts(serverPort, CancellationToken);
+			Logger.Debug("Detected {OnlinePlayerCount} / {MaximumPlayerCount} online player(s).", result.Online, result.Maximum);
+			return result;
 		} catch (ServerStatusProtocol.ProtocolException e) {
 			Logger.Error(e.Message);
 			return null;
@@ -88,12 +96,12 @@ sealed class InstancePlayerCountTracker : CancellableBackgroundTask {
 		var onlinePlayersDetected = AsyncTasks.CreateCompletionSource();
 
 		lock (this) {
-			if (onlinePlayerCount == null) {
-				throw new InvalidOperationException();
-			}
-			else if (onlinePlayerCount > 0) {
+			if (playerCounts is { Online: > 0 }) {
 				return;
 			}
+			else if (playerCounts == null) {
+				throw new InvalidOperationException();
+			}
 
 			OnlinePlayerCountChanged += OnOnlinePlayerCountChanged;
 
@@ -123,7 +131,7 @@ sealed class InstancePlayerCountTracker : CancellableBackgroundTask {
 	protected override void Dispose() {
 		lock (this) {
 			isDisposed = true;
-			onlinePlayerCount = null;
+			playerCounts = null;
 		}
 
 		process.RemoveOutputListener(OnOutput);
diff --git a/Common/Phantom.Common.Data.Web/Instance/Instance.cs b/Common/Phantom.Common.Data.Web/Instance/Instance.cs
index 908580d..9fd25fb 100644
--- a/Common/Phantom.Common.Data.Web/Instance/Instance.cs
+++ b/Common/Phantom.Common.Data.Web/Instance/Instance.cs
@@ -8,9 +8,10 @@ public sealed partial record Instance(
 	[property: MemoryPackOrder(0)] Guid InstanceGuid,
 	[property: MemoryPackOrder(1)] InstanceConfiguration Configuration,
 	[property: MemoryPackOrder(2)] IInstanceStatus Status,
-	[property: MemoryPackOrder(3)] bool LaunchAutomatically
+	[property: MemoryPackOrder(3)] InstancePlayerCounts? PlayerCounts,
+	[property: MemoryPackOrder(4)] bool LaunchAutomatically
 ) {
 	public static Instance Offline(Guid instanceGuid, InstanceConfiguration configuration, bool launchAutomatically = false) {
-		return new Instance(instanceGuid, configuration, InstanceStatus.Offline, launchAutomatically);
+		return new Instance(instanceGuid, configuration, InstanceStatus.Offline, PlayerCounts: null, launchAutomatically);
 	}
 }
diff --git a/Common/Phantom.Common.Data/Instance/InstancePlayerCounts.cs b/Common/Phantom.Common.Data/Instance/InstancePlayerCounts.cs
new file mode 100644
index 0000000..1350138
--- /dev/null
+++ b/Common/Phantom.Common.Data/Instance/InstancePlayerCounts.cs
@@ -0,0 +1,9 @@
+using MemoryPack;
+
+namespace Phantom.Common.Data.Instance;
+
+[MemoryPackable(GenerateType.VersionTolerant)]
+public readonly partial record struct InstancePlayerCounts(
+	[property: MemoryPackOrder(0)] int Online,
+	[property: MemoryPackOrder(1)] int Maximum
+);
diff --git a/Common/Phantom.Common.Messages.Agent/AgentMessageRegistries.cs b/Common/Phantom.Common.Messages.Agent/AgentMessageRegistries.cs
index c3b9c3a..305f2cd 100644
--- a/Common/Phantom.Common.Messages.Agent/AgentMessageRegistries.cs
+++ b/Common/Phantom.Common.Messages.Agent/AgentMessageRegistries.cs
@@ -31,6 +31,7 @@ public static class AgentMessageRegistries {
 		ToController.Add<InstanceOutputMessage>(5);
 		ToController.Add<ReportAgentStatusMessage>(6);
 		ToController.Add<ReportInstanceEventMessage>(7);
+		ToController.Add<ReportInstancePlayerCountsMessage>(8);
 		ToController.Add<ReplyMessage>(127);
 	}
 
diff --git a/Common/Phantom.Common.Messages.Agent/ToController/ReportInstancePlayerCountsMessage.cs b/Common/Phantom.Common.Messages.Agent/ToController/ReportInstancePlayerCountsMessage.cs
new file mode 100644
index 0000000..e71efd5
--- /dev/null
+++ b/Common/Phantom.Common.Messages.Agent/ToController/ReportInstancePlayerCountsMessage.cs
@@ -0,0 +1,10 @@
+using MemoryPack;
+using Phantom.Common.Data.Instance;
+
+namespace Phantom.Common.Messages.Agent.ToController;
+
+[MemoryPackable(GenerateType.VersionTolerant)]
+public sealed partial record ReportInstancePlayerCountsMessage(
+	[property: MemoryPackOrder(0)] Guid InstanceGuid,
+	[property: MemoryPackOrder(1)] InstancePlayerCounts? PlayerCounts
+) : IMessageToController;
diff --git a/Controller/Phantom.Controller.Services/Agents/AgentActor.cs b/Controller/Phantom.Controller.Services/Agents/AgentActor.cs
index 46c17fa..ed92d02 100644
--- a/Controller/Phantom.Controller.Services/Agents/AgentActor.cs
+++ b/Controller/Phantom.Controller.Services/Agents/AgentActor.cs
@@ -96,6 +96,7 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 		Receive<UpdateJavaRuntimesCommand>(UpdateJavaRuntimes);
 		ReceiveAndReplyLater<CreateOrUpdateInstanceCommand, Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstance);
 		Receive<UpdateInstanceStatusCommand>(UpdateInstanceStatus);
+		Receive<UpdateInstancePlayerCountsCommand>(UpdateInstancePlayerCounts);
 		ReceiveAndReplyLater<LaunchInstanceCommand, Result<LaunchInstanceResult, InstanceActionFailure>>(LaunchInstance);
 		ReceiveAndReplyLater<StopInstanceCommand, Result<StopInstanceResult, InstanceActionFailure>>(StopInstance);
 		ReceiveAndReplyLater<SendCommandToInstanceCommand, Result<SendCommandToInstanceResult, InstanceActionFailure>>(SendMinecraftCommand);
@@ -159,7 +160,7 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 	private async Task<ImmutableArray<ConfigureInstanceMessage>> PrepareInitialConfigurationMessages() {
 		var configurationMessages = ImmutableArray.CreateBuilder<ConfigureInstanceMessage>();
 		
-		foreach (var (instanceGuid, instanceConfiguration, _, launchAutomatically) in instanceDataByGuid.Values.ToImmutableArray()) {
+		foreach (var (instanceGuid, instanceConfiguration, _, _, launchAutomatically) in instanceDataByGuid.Values.ToImmutableArray()) {
 			var serverExecutableInfo = await minecraftVersions.GetServerExecutableInfo(instanceConfiguration.MinecraftVersion, cancellationToken);
 			configurationMessages.Add(new ConfigureInstanceMessage(instanceGuid, instanceConfiguration, new InstanceLaunchProperties(serverExecutableInfo), launchAutomatically));
 		}
@@ -186,6 +187,8 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 	public sealed record CreateOrUpdateInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, InstanceConfiguration Configuration) : ICommand, ICanReply<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>;
 	
 	public sealed record UpdateInstanceStatusCommand(Guid InstanceGuid, IInstanceStatus Status) : ICommand;
+	
+	public sealed record UpdateInstancePlayerCountsCommand(Guid InstanceGuid, InstancePlayerCounts? PlayerCounts) : ICommand;
 
 	public sealed record LaunchInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid) : ICommand, ICanReply<Result<LaunchInstanceResult, InstanceActionFailure>>;
 	
@@ -341,6 +344,10 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 	private void UpdateInstanceStatus(UpdateInstanceStatusCommand command) {
 		TellInstance(command.InstanceGuid, new InstanceActor.SetStatusCommand(command.Status));
 	}
+	
+	private void UpdateInstancePlayerCounts(UpdateInstancePlayerCountsCommand command) {
+		TellInstance(command.InstanceGuid, new InstanceActor.SetPlayerCountsCommand(command.PlayerCounts));
+	}
 
 	private Task<Result<LaunchInstanceResult, InstanceActionFailure>> LaunchInstance(LaunchInstanceCommand command) {
 		return RequestInstance<InstanceActor.LaunchInstanceCommand, LaunchInstanceResult>(command.InstanceGuid, new InstanceActor.LaunchInstanceCommand(command.LoggedInUserGuid));
diff --git a/Controller/Phantom.Controller.Services/Instances/InstanceActor.cs b/Controller/Phantom.Controller.Services/Instances/InstanceActor.cs
index 267ebce..8e7f5aa 100644
--- a/Controller/Phantom.Controller.Services/Instances/InstanceActor.cs
+++ b/Controller/Phantom.Controller.Services/Instances/InstanceActor.cs
@@ -26,6 +26,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
 	
 	private InstanceConfiguration configuration;
 	private IInstanceStatus status;
+	private InstancePlayerCounts? playerCounts;
 	private bool launchAutomatically;
 
 	private readonly ActorRef<InstanceDatabaseStorageActor.ICommand> databaseStorageActor;
@@ -35,11 +36,12 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
 		this.agentConnection = init.AgentConnection;
 		this.cancellationToken = init.CancellationToken;
 		
-		(this.instanceGuid, this.configuration, this.status, this.launchAutomatically) = init.Instance;
+		(this.instanceGuid, this.configuration, this.status, this.playerCounts, this.launchAutomatically) = init.Instance;
 
 		this.databaseStorageActor = Context.ActorOf(InstanceDatabaseStorageActor.Factory(new InstanceDatabaseStorageActor.Init(instanceGuid, init.DbProvider, init.CancellationToken)), "DatabaseStorage");
 
 		Receive<SetStatusCommand>(SetStatus);
+		Receive<SetPlayerCountsCommand>(SetPlayerCounts);
 		ReceiveAsyncAndReply<ConfigureInstanceCommand, Result<ConfigureInstanceResult, InstanceActionFailure>>(ConfigureInstance);
 		ReceiveAsyncAndReply<LaunchInstanceCommand, Result<LaunchInstanceResult, InstanceActionFailure>>(LaunchInstance);
 		ReceiveAsyncAndReply<StopInstanceCommand, Result<StopInstanceResult, InstanceActionFailure>>(StopInstance);
@@ -47,7 +49,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
 	}
 
 	private void NotifyInstanceUpdated() {
-		agentActor.Tell(new AgentActor.ReceiveInstanceDataCommand(new Instance(instanceGuid, configuration, status, launchAutomatically)));
+		agentActor.Tell(new AgentActor.ReceiveInstanceDataCommand(new Instance(instanceGuid, configuration, status, playerCounts, launchAutomatically)));
 	}
 
 	private void SetLaunchAutomatically(bool newValue) {
@@ -65,6 +67,8 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
 	public interface ICommand {}
 
 	public sealed record SetStatusCommand(IInstanceStatus Status) : ICommand;
+	
+	public sealed record SetPlayerCountsCommand(InstancePlayerCounts? PlayerCounts) : ICommand;
 
 	public sealed record ConfigureInstanceCommand(Guid AuditLogUserGuid, Guid InstanceGuid, InstanceConfiguration Configuration, InstanceLaunchProperties LaunchProperties, bool IsCreatingInstance) : ICommand, ICanReply<Result<ConfigureInstanceResult, InstanceActionFailure>>;
 
@@ -76,6 +80,16 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
 
 	private void SetStatus(SetStatusCommand command) {
 		status = command.Status;
+		
+		if (!status.IsRunning() && status != InstanceStatus.Offline /* Guard against temporary disconnects */) {
+			playerCounts = null;
+		}
+		
+		NotifyInstanceUpdated();
+	}
+	
+	private void SetPlayerCounts(SetPlayerCountsCommand command) {
+		playerCounts = command.PlayerCounts;
 		NotifyInstanceUpdated();
 	}
 
diff --git a/Controller/Phantom.Controller.Services/Rpc/AgentMessageHandlerActor.cs b/Controller/Phantom.Controller.Services/Rpc/AgentMessageHandlerActor.cs
index 19225c3..d6c6d43 100644
--- a/Controller/Phantom.Controller.Services/Rpc/AgentMessageHandlerActor.cs
+++ b/Controller/Phantom.Controller.Services/Rpc/AgentMessageHandlerActor.cs
@@ -39,6 +39,7 @@ sealed class AgentMessageHandlerActor : ReceiveActor<IMessageToController> {
 		Receive<AdvertiseJavaRuntimesMessage>(HandleAdvertiseJavaRuntimes);
 		Receive<ReportAgentStatusMessage>(HandleReportAgentStatus);
 		Receive<ReportInstanceStatusMessage>(HandleReportInstanceStatus);
+		Receive<ReportInstancePlayerCountsMessage>(HandleReportInstancePlayerCounts);
 		Receive<ReportInstanceEventMessage>(HandleReportInstanceEvent);
 		Receive<InstanceOutputMessage>(HandleInstanceOutput);
 		Receive<ReplyMessage>(HandleReply);
@@ -74,6 +75,10 @@ sealed class AgentMessageHandlerActor : ReceiveActor<IMessageToController> {
 		agentManager.TellAgent(agentGuid, new AgentActor.UpdateInstanceStatusCommand(message.InstanceGuid, message.InstanceStatus));
 	}
 
+	private void HandleReportInstancePlayerCounts(ReportInstancePlayerCountsMessage message) {
+		agentManager.TellAgent(agentGuid, new AgentActor.UpdateInstancePlayerCountsCommand(message.InstanceGuid, message.PlayerCounts));
+	}
+
 	private void HandleReportInstanceEvent(ReportInstanceEventMessage message) {
 		message.Event.Accept(eventLogManager.CreateInstanceEventVisitor(message.EventGuid, message.UtcTime, agentGuid, message.InstanceGuid));
 	}
diff --git a/Web/Phantom.Web/Pages/Instances.razor b/Web/Phantom.Web/Pages/Instances.razor
index 0e4bdbb..69507ea 100644
--- a/Web/Phantom.Web/Pages/Instances.razor
+++ b/Web/Phantom.Web/Pages/Instances.razor
@@ -21,6 +21,7 @@
     <Column Width="40%">Agent</Column>
     <Column Width="40%">Name</Column>
     <Column MinWidth="215px">Status</Column>
+    <Column Class="text-center" MinWidth="120px">Players</Column>
     <Column Width="20%">Version</Column>
     <Column Class="text-center" MinWidth="110px">Server Port</Column>
     <Column Class="text-center" MinWidth="110px">Rcon Port</Column>
@@ -40,6 +41,14 @@
     <Cell>
       <InstanceStatusText Status="instance.Status" />
     </Cell>
+    <Cell class="text-center">
+      @if (instance.PlayerCounts is var (online, maximum)) {
+	      <p class="font-monospace">@online.ToString() / @maximum.ToString()</p>
+      }
+      else {
+	      <p class="font-monospace">-</p>
+      }
+    </Cell>
     <Cell>@configuration.MinecraftServerKind @configuration.MinecraftVersion</Cell>
     <Cell class="text-center">
       <p class="font-monospace">@configuration.ServerPort.ToString()</p>