Open-source, self-hostable remote control and remote access.
Demo Server: https://controlr.app
Docker: https://hub.docker.com/r/translucency/controlr
DeepWiki: https://deepwiki.com/bitbound/ControlR
Discussions: https://github.com/bitbound/ControlR/discussions
Project Board: https://github.com/users/bitbound/projects/1
wget https://raw.githubusercontent.com/bitbound/ControlR/main/docker-compose/docker-compose.yml
sudo docker compose up -d
IMPORTANT: Read the below sections regarding reverse proxy and Cloudflare caching.
At minimum, you will need to supply environment variables (e.g. ControlR_POSTGRES_USER
) for the anchors (e.g. $pgUser
) at the top of the docker-compose file. Alternatively, you could also use Docker secrets, an environment file, or hard-code them in the docker-compose file. Whatever works for your setup and security requirements.
See the comments in the docker-compose file for additional configuration info.
Afterward, ControlR should be available on port 5120 (by default). Running curl http://127.0.0.1:5120/health
should return "Healthy."
Some ControlR features require forwarded headers. These concepts are not unique to ASP.NET Core, so it's important to understand them when self-hosting.
When using a reverse proxy, including Cloudflare proxying, the proxy IPs must be trusted by the service receiving the forwarded traffic. By default, ControlR will trust the Docker gateway IP. If EnableCloudflareProxySupport
option is enabled, the Cloudflare IP ranges will automatically be trusted too.
Every proxy server IP needs to be added to the X-Forwarded-For
header, creating a chain of all hops until it reaches the service that handles the request. Each proxy server in the chain needs to trust all IPs that came before it. When the request reaches the service, the header should have a complete chain of all proxy servers.
If you have another reverse proxy in front of Docker (e.g., Nginx, Caddy, etc.), it must trust the IPs of any proxies that came before it (e.g., Cloudflare). Likewise, your service in Docker (i.e., ControlR) must also trust the IP of your reverse proxy. If the reverse proxy is on the same machine as the service and is forwarding to localhost, the service will automatically trust it.
Additional proxy IPs can be added to the KnownProxies
list in the docker-compose file.
If the public IP for your connected devices is not showing correctly, the problem is likely due to a misconfiguration here.
ControlR relies on custom headers returned by HEAD
requests for files under the /downloads
path. If you're using Cloudflare proxy, you must bypass the cache for all requests under /downloads
.
By default, the server is single-tenant (although you can organize customer tenants via tags). The first user created will be the server and tenant administrator, and subsequent accounts must be explicitly created by the tenant admin.
Setting ControlR_AppOptions__EnablePublicRegistration
to true
in the docker-compose file will allow anyone to create a new account on the server. A new tenant is created for each account created this way.
The database uses EF Core's Global Query Filters feature to isolate tenant data (devices, users, etc.);
- Full remote control support
- Full remote control support
- Controlling the login window is only possible after a user has logged in
- Experimental remote control via VNC (Apple Screen Sharing)
- Signed using adhoc certificate (this may change in the future)
- Experimental remote control via VNC (Apple Screen Sharing)
- Signed using adhoc certificate (this may change in the future)
- Full remote control support on X11
- Tested on Ubuntu, Kubuntu, and Mint
- On Ubuntu, you must enable X11 for the login screen
- Edit
/etc/gdm3/custom.conf
and uncomment the lineWaylandEnable=false
, then reboot
- Edit
- Experimental remote control via VNC
- Terminal uses embedded cross-platform PowerShell host
Logs for the agent and desktop client are detailed below. On Windows, the path depends on whether the app is running in Debug or Release mode. On macOS and Linux, the path depends on whether the app is running as root. This is only relevant for local debugging.
Under normal user circumstances, the main agent will run in Release mode as SYSTEM/root. For Mac and Ubuntu, the desktop client will normally run as the user for the current GUI session. For Windows, the desktop client runs as SYSTEM due to permissions required for capturing and controlling full-screen UAC prompts and the WinLogon desktop.
Main Agent
- Windows
- Release:
C:\ProgramData\ControlR\{hostname}\Logs\ControlR.Agent\LogFile.log
- Debug:
C:\ProgramData\ControlR\Debug\{hostname}\Logs\ControlR.Agent\LogFile.log
- Release:
- macOS / Linux
- Running as root:
/var/log/controlr/{hostname}/ControlR.Agent/LogFile.log
- Running as user:
~/.controlr/logs/{hostname}/ControlR.Agent/LogFile.log
- Running as root:
Desktop Client
- Windows
- Release:
C:\ProgramData\ControlR\{hostname}\Logs\ControlR.DesktopClient\LogFile.log
- Debug:
C:\ProgramData\ControlR\Debug\{hostname}\Logs\ControlR.DesktopClient\LogFile.log
- Release:
- macOS / Linux
- Running as root:
/var/log/controlr/{hostname}/ControlR.DesktopClient/LogFile.log
- Running as user:
~/.controlr/logs/{hostname}/ControlR.DesktopClient/LogFile.log
- Running as root:
Permissions are implemented via a combination of role-based and resource-based authorization. When the first account is created, all roles are assigned. Subsequent accounts must be explicitly assigned roles.
To access a device, a user must have either the DeviceSuperuser
role or a matching tag. Tags can be assigned to both users and devices to grant access.
Role Descriptions:
AgentInstaller
- Able to deploy/install the agent on new devices
DeviceSuperUser
- Able to access all devices
TenantAdministrator
- Able to manage users and permissions for the tenant
ServerAdministrator
- Able to manage and see stats for the server
- This does not allow access to other tenants' devices or users
An OpenAPI spec is created with each build of the server and committed to the repository. It can be found here, or within the artifacts for each GitHub release. You can use this file to generate API clients in any language.
While debugging, you can also browse the API at https://localhost:7033/scalar/ or https://localhost:7033/openapi/v1.json.
Personal Access Tokens (PATs) allow you to authenticate with the ControlR API as your user account without using a username and password. They can be used for integrations, scripts, automation, and other scenarios where you need to authenticate programmatically.
To create a PAT, follow these steps:
- Go to the Access Tokens page in the ControlR web interface.
- Enter a friendly name for your token.
- Click the Create PAT button.
- Copy the generated token. Make sure to store it securely, as it will never be shown again.
- Add the token to the
x-personal-token
header in your API requests.
Logon Tokens are time-limited, single-use tokens that allow you to create an authenticated browser session with a specific device. Coupled with PATs, this allows you to create an integration that can open a browser tab to access a particular device.
See the /api/logon-tokens
endpoint in the API spec. A successful response includes the full URL, including the logon token, that can be opened in the browser to access the target device.
Remember that the token is single-use, so the URL can only be accessed once.
Logs, traces, and metrics will be sent to the Aspire Dashboard container. The web interface is exposed on port 18888, and it's secured by the aspireToken
value.
The dashboard also supports OpenIdConnect authentication. See their readme for more information.
You can also add a connection string for Azure Monitor to see your data there. This can be used in combination with the Aspire Dashboard (OTLP) or on its own.
ControlR is able to integrate with geographically-distributed relay servers and transfer remote control sessions to a server closest to you. See the comments in the Docker Compose file for configuration information.
Relay servers are currently disabled on the public server (https://controlr.app), which is located in Seattle, WA.
This is an experimental feature that allows you to control Mac and Linux devices using VNC. The noVNC client is used for the front-end, and the connection is streamed via websockets through the ControlR server, to the agent, then to the VNC server on the device.
Since the connection to the VNC server is over localhost, you can configure the VNC server to bind to the loopback interface, so it's not exposed to the local network.
You can configure the VNC port in the agent's appsettings.json file, under AppOptions:VncPort
. If it is omitted, it will use the default VNC port of 5900.
If the project gains support, I intend to add full remote control support for Mac.