7 Commits

Author SHA1 Message Date
GL.iNet-Yongping.Xie ed3da45ddf feat: release version v1.0.1
1. Published official Docker images to Docker Hub.
2. Added docker-compose configuration template for easier deployment.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-15 21:00:58 -07:00
GL.iNet-Yongping.Xie b089169270 doc: update docker-compose YAML file
1. Official image has been pushed to Docker Hub.
2. Updated the docker-compose YAML file to reference the official image.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-15 19:17:00 -07:00
GL.iNet-Yongping.Xie 2349554c22 doc: update docker-compose startup instructions
1. Updated the documentation for starting services with docker-compose,
   making the description clearer and more accurate.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-15 01:24:49 -07:00
GL.iNet-Yongping.Xie 77886cf052 doc: update docker-compose startup instructions
1. Updated the documentation for starting services with docker-compose,
   making the description clearer and more accurate.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-15 01:19:29 -07:00
GL.iNet-Yongping.Xie 9be7a854ce feat: add initial docker-compose.yml for testing
1. Added support for docker-compose.yml to simplify deployment and
   service management.
2. Tested environment variable configurations to validate compatibility
   and ensure correct behavior.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-15 01:05:21 -07:00
GL.iNet-Yongping.Xie 2b6aecf72a fix: remove docker-compose.yml file
1. The glkvm-cloud image has not yet been pushed to the repository and
   cannot be used directly.
2. Temporarily removed the docker-compose.yml file to prevent customers
   from misusing it.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-10 01:08:44 -07:00
GL.iNet-Yongping.Xie efa73caf75 fix: update Chinese and English README
1. Added clarification about dedicated bandwidth for self-hosted
   deployments.
2. Corrected grammar errors in the English documentation.
3. Added server bandwidth configuration requirements to guide proper
   deployment.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-09-07 20:08:56 -07:00
20 changed files with 262 additions and 174 deletions
+7 -3
View File
@@ -36,6 +36,10 @@ build-run: build run
# Build frontend, build Go binary, and run
full-run: ui build run
# Build Docker image
docker-build: ui build
docker build -t glkvm:v1 .
# Build Docker image without updating ui
docker-build: build
docker build -t glkvm-cloud:build .
# Full Build Docker image
docker-fullbuild: ui build
docker build -t glkvm-cloud:build .
+14 -2
View File
@@ -13,6 +13,7 @@ Self-Deployed Lightweight Cloud is a lightweight KVM remote cloud platform tailo
- **Batch Operations** - Batch command execution capabilities
- **Rapid Deployment** - Quick self-deployment with simple operations
- **Data Security** - Private deployment with full data control
- **Dedicated Bandwidth** - Exclusive bandwidth for self-hosted deployments
- **Lightweight Design** - Optimized for small businesses and individual users
## Self-Hosting Guide
@@ -34,9 +35,10 @@ The following mainstream operating systems have been tested and verified
| Component | Minimum Requirement |
| :-----------------: | :-----------------: |
| CPU | 1 cores or above |
| CPU | 1 core or above |
| Memory | ≥ 1 GB |
| Storage | ≥ 40 GB |
| Network Bandwidth | ≥ 3 Mbps |
| KVM Device Firmware | ≥ v1.5.0 |
#### 🔐 Cloud Security Group Settings
@@ -56,12 +58,22 @@ If your server provider uses a **cloud security group** (e.g., AWS, Aliyun, etc.
------
### 📦 Installation
Run the following command **as root** to install GLKVM Cloud:
We provide **two** ways to install GLKVM Cloud:
#### A) One-line installer (recommended)
> **Note:** The one-line installer is **Docker-based**. It automates Docker/Compose setup, pulls images, renders configs from templates, and starts services for you.
Run **as root**:
```bash
( command -v curl >/dev/null 2>&1 && curl -fsSL https://kvm-cloud.gl-inet.com/selfhost/install.sh || wget -qO- https://kvm-cloud.gl-inet.com/selfhost/install.sh ) | sudo bash
```
#### B) Docker manual install
> Full reference: see [`docker-compose/README.md`](https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/README.md)
### 🌐 Platform Access
Once the installation is complete, access the platform via:
+3 -1
View File
@@ -14,6 +14,7 @@
* **批量操作** - 支持批量执行命令
* **快速部署** - 简单命令即可完成自部署
* **数据安全** - 私有化部署,数据完全可控
* **独享带宽** - 自部署环境下可享受专属带宽
* **轻量设计** - 专为小型企业和个人优化
## 自部署指南
@@ -34,10 +35,11 @@
#### 系统要求
| 组件 | 最低配置要求 |
| ------------ | ------------ |
| :------------: | :------------: |
| CPU | 1 核及以上 |
| 内存 | ≥ 1 GB |
| 存储 | ≥ 40 GB |
| 网络带宽 | ≥ 3 Mbps |
| KVM 固件版本 | ≥ v1.5.0 |
#### 🔐 云安全组端口要求
+16 -4
View File
@@ -295,15 +295,23 @@ func (srv *RttyServer) ListenAPI() error {
host := c.Request.Host
hostname, _, err := net.SplitHostPort(host)
if err != nil {
// 没有端口时直接使用 host
hostname = host
hostname = host // Use host directly if no port
}
// Choose value by priority:
// 1) If request host is a domain (not an IP), keep it.
// 2) Else if it's an IP and cfg.WebrtcIP is set, use cfg.WebrtcIP.
// 3) Else keep the request IP.
chosen := hostname
if isIP(hostname) && cfg.WebrtcIP != "" {
chosen = cfg.WebrtcIP
}
c.JSON(http.StatusOK, gin.H{
"hostname": hostname,
"hostname": chosen, // reuse the same chosen value
"port": cfg.AddrDev,
"token": cfg.Token,
"webrtcIP": cfg.WebrtcIP,
"webrtcIP": chosen, // same as hostname
"webrtcPort": cfg.WebrtcPort,
"webrtcUsername": cfg.WebrtcUsername,
"webrtcPassword": cfg.WebrtcPassword,
@@ -332,6 +340,10 @@ func (srv *RttyServer) ListenAPI() error {
return r.RunListener(ln)
}
func isIP(addr string) bool {
return net.ParseIP(addr) != nil
}
func callUserHookUrl(cfg *Config, c *gin.Context) bool {
if cfg.UserHookUrl == "" {
return true
-27
View File
@@ -1,27 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G
h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV
6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw
gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj
v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB
AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g
BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu
Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc
MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL
pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp
eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH
pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7
s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu
h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv
YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8
ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0
LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+
EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY
Ig46v9mFmBvyH04=
-----END CERTIFICATE-----
@@ -1,22 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAxmgAwIBAgISBQsXxrfK6EpotI2fXWteV3kMMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
NjAeFw0yNTA3MjgwNTM5MjNaFw0yNTEwMjYwNTM5MjJaMBcxFTATBgNVBAMTDGNs
YW54aWUubGlmZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKcExKR+nZ21Hpm6
rqR/QuJ35yRwhBtTim+5cColIDIocjasMhB1ho49IINEa/hp5EHqTsMsgSkGEZ6R
unr3D0qjggIoMIICJDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKlsFBhG/CpyaZvz
3/gFZdP0vxsWMB8GA1UdIwQYMBaAFJMnRpgDqVFojpjWxEJI2yO/WJTSMDIGCCsG
AQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL2U2LmkubGVuY3Iub3JnLzAn
BgNVHREEIDAegg4qLmNsYW54aWUubGlmZYIMY2xhbnhpZS5saWZlMBMGA1UdIAQM
MAowCAYGZ4EMAQIBMC0GA1UdHwQmMCQwIqAgoB6GHGh0dHA6Ly9lNi5jLmxlbmNy
Lm9yZy8xMy5jcmwwggECBgorBgEEAdZ5AgQCBIHzBIHwAO4AdQDtPEvW6AbCpKIA
V9vLJOI4Ad9RL+3EhsVwDyDdtz4/4AAAAZhPwIK1AAAEAwBGMEQCIGoi+WQh9Bhb
4f68AFmojUBHdj0cJhH8Wgv26QxqKUXZAiAyrxtJ9viGLjivjDUPm8NjmgquuLeH
UAMkhSdvZmKK1QB1AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0AAAB
mE/AmiYAAAQDAEYwRAIgbXMFtU65Mrr1ZaQSdX3Jc+5YO1Y/yApwx7vbGKOzriQC
IFgfpun9rGzPSXUBW+oEjkvUeN1Yf2Thu4YIvkUfuceiMAoGCCqGSM49BAMDA2gA
MGUCMCU9wGtvD/A/LqIdeNFtS8/Um7Sv0iyAn8JGcAu/GCI2oavEQk90mPvqMuqX
W2rUQgIxANHH0WTA3121/bK4Rhu5w8kXUI8AWnxizZ4hDjGCKi6WmJCGhLqtleYK
hhrMK8WjmQ==
-----END CERTIFICATE-----
@@ -1,15 +0,0 @@
Le_Domain='clanxie.life'
Le_Alt='*.clanxie.life'
Le_Webroot='dns_cf'
Le_PreHook=''
Le_PostHook=''
Le_RenewHook=''
Le_API='https://acme-v02.api.letsencrypt.org/directory'
Le_Keylength='ec-256'
Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/2400063957/411715523371'
Le_LinkOrder='https://acme-v02.api.letsencrypt.org/acme/order/2400063957/411715523371'
Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/050b17c6b7cae84a68b48d9f5d6b5e57790c'
Le_CertCreateTime='1753684679'
Le_CertCreateTimeStr='2025-07-28T06:37:59Z'
Le_NextRenewTimeStr='2025-09-25T06:37:59Z'
Le_NextRenewTime='1758782279'
@@ -1,9 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBKzCB0gIBADAXMRUwEwYDVQQDDAxjbGFueGllLmxpZmUwWTATBgcqhkjOPQIB
BggqhkjOPQMBBwNCAASnBMSkfp2dtR6Zuq6kf0Lid+ckcIQbU4pvuXAqJSAyKHI2
rDIQdYaOPSCDRGv4aeRB6k7DLIEpBhGekbp69w9KoFkwVwYJKoZIhvcNAQkOMUow
SDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwJwYDVR0RBCAwHoIMY2xh
bnhpZS5saWZlgg4qLmNsYW54aWUubGlmZTAKBggqhkjOPQQDAgNIADBFAiEAvyNm
66wN/4Ni5cLfH8KpwHcgoVGXDoVAawiVaMdmsD0CIAKsdhG5SWWqvNYhYjKIVKbh
Fv8cJ8R1rsaQPO98zj5k
-----END CERTIFICATE REQUEST-----
@@ -1,8 +0,0 @@
[ req_distinguished_name ]
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ v3_req ]
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=DNS:clanxie.life,DNS:*.clanxie.life
@@ -1,5 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJW3lmfzDnyLpgkX0Jlu0J3Bo1OyVa610GHAdvQmah8loAoGCCqGSM49
AwEHoUQDQgAEpwTEpH6dnbUembqupH9C4nfnJHCEG1OKb7lwKiUgMihyNqwyEHWG
jj0gg0Rr+GnkQepOwyyBKQYRnpG6evcPSg==
-----END EC PRIVATE KEY-----
@@ -1,49 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAxmgAwIBAgISBQsXxrfK6EpotI2fXWteV3kMMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
NjAeFw0yNTA3MjgwNTM5MjNaFw0yNTEwMjYwNTM5MjJaMBcxFTATBgNVBAMTDGNs
YW54aWUubGlmZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKcExKR+nZ21Hpm6
rqR/QuJ35yRwhBtTim+5cColIDIocjasMhB1ho49IINEa/hp5EHqTsMsgSkGEZ6R
unr3D0qjggIoMIICJDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKlsFBhG/CpyaZvz
3/gFZdP0vxsWMB8GA1UdIwQYMBaAFJMnRpgDqVFojpjWxEJI2yO/WJTSMDIGCCsG
AQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL2U2LmkubGVuY3Iub3JnLzAn
BgNVHREEIDAegg4qLmNsYW54aWUubGlmZYIMY2xhbnhpZS5saWZlMBMGA1UdIAQM
MAowCAYGZ4EMAQIBMC0GA1UdHwQmMCQwIqAgoB6GHGh0dHA6Ly9lNi5jLmxlbmNy
Lm9yZy8xMy5jcmwwggECBgorBgEEAdZ5AgQCBIHzBIHwAO4AdQDtPEvW6AbCpKIA
V9vLJOI4Ad9RL+3EhsVwDyDdtz4/4AAAAZhPwIK1AAAEAwBGMEQCIGoi+WQh9Bhb
4f68AFmojUBHdj0cJhH8Wgv26QxqKUXZAiAyrxtJ9viGLjivjDUPm8NjmgquuLeH
UAMkhSdvZmKK1QB1AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0AAAB
mE/AmiYAAAQDAEYwRAIgbXMFtU65Mrr1ZaQSdX3Jc+5YO1Y/yApwx7vbGKOzriQC
IFgfpun9rGzPSXUBW+oEjkvUeN1Yf2Thu4YIvkUfuceiMAoGCCqGSM49BAMDA2gA
MGUCMCU9wGtvD/A/LqIdeNFtS8/Um7Sv0iyAn8JGcAu/GCI2oavEQk90mPvqMuqX
W2rUQgIxANHH0WTA3121/bK4Rhu5w8kXUI8AWnxizZ4hDjGCKi6WmJCGhLqtleYK
hhrMK8WjmQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G
h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV
6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw
gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj
v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB
AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g
BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu
Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc
MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL
pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp
eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH
pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7
s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu
h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv
YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8
ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0
LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+
EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY
Ig46v9mFmBvyH04=
-----END CERTIFICATE-----
-29
View File
@@ -1,29 +0,0 @@
version: '3.8'
services:
rttys:
container_name: glkvm_cloud
image: glkvm:v1
ports:
- "443:443"
- "10443:10443"
- "5912:5912"
volumes:
- ./rttys.conf:/home/rttys.conf:ro
- ./certificate/glkvm.cer:/home/certificate/glkvm_cer:ro
- ./certificate/glkvm.key:/home/certificate/glkvm_key:ro
command: ["-c", "/home/rttys.conf"]
restart: always
coturn:
image: coturn/coturn:edge-alpine
container_name: glkvm_coturn
restart: always
ports:
- "3478:3478"
- "3478:3478/udp"
- "5349:5349"
- "5349:5349/udp"
volumes:
- ./turnserver.conf:/etc/turnserver.conf:ro
command: ["-c", "/etc/turnserver.conf"]
+19
View File
@@ -0,0 +1,19 @@
# Images
GLKVM_IMAGE=glzhitong/glkvm-cloud:latest
COTURN_IMAGE=coturn/coturn:edge-alpine
# GLKVM access IP seen by devices/users.
# Leave empty to auto-detect at container start.
GLKVM_ACCESS_IP=
# rttys
RTTYS_TOKEN=DeviceTokenYouCanChangeMe
RTTYS_PASS=StrongP@ssw0rd
RTTYS_DEVICE_PORT=5912
RTTYS_WEBUI_PORT=443
RTTYS_HTTP_PROXY_PORT=10443
# TURN
TURN_PORT=3478
TURN_USER=glkvmcloudwebrtcuser
TURN_PASS=AnotherS3cret
+43
View File
@@ -0,0 +1,43 @@
# Quick Start
This guide shows how to deploy **glkvm-cloud** using the provided Docker Compose environment template.
1. **Clone the repository and prepare the environment template**
```bash
git clone https://github.com/gl-inet/glkvm-cloud.git
cd glkvm-cloud/docker-compose/
cp .env.example .env
```
2. **Configure environment variables**
Edit `.env` and update the required parameters:
- `RTTYS_TOKEN`: device connection token (leave empty to use the default)
- `RTTYS_PASS`: web management password (leave empty to use the default **StrongP@ssw0rd**)
- `TURN_USER` / `TURN_PASS`: coturn authentication credentials (leave empty to use the default)
- `GLKVM_ACCESS_IP`: glkvm cloud access address (leave empty to auto-detect at startup)
⚠️ **Note:** All configuration should be done in the `.env` file.
You don’t need to modify `docker-compose.yml`, templates, or scripts directly.
3. **Start the services**
```bash
docker-compose up -d
```
If you modify `.env` or template files, make sure to apply the updates:
```bash
docker-compose down && docker-compose up -d
```
4. **Platform Access**
Once the installation is complete, access the platform via:
```bash
https://<your_server_public_ip>
```
+54
View File
@@ -0,0 +1,54 @@
version: "2.0"
services:
rttys:
image: ${GLKVM_IMAGE:-glzhitong/glkvm-cloud:latest}
container_name: glkvm_cloud
restart: always
environment:
# Preferred: set GLKVM_ACCESS_IP explicitly; if empty, entrypoint will auto-detect once.
GLKVM_ACCESS_IP: ${GLKVM_ACCESS_IP:-}
# ---- rttys ----
RTTYS_TOKEN: ${RTTYS_TOKEN:-DeviceTokenYouCanChangeMe}
RTTYS_PASS: ${RTTYS_PASS:-StrongP@ssw0rd}
# Ports inside container (mirrored to host via `ports` below)
RTTYS_DEVICE_PORT: ${RTTYS_DEVICE_PORT:-5912} # addr-dev
RTTYS_WEBUI_PORT: ${RTTYS_WEBUI_PORT:-443} # addr-user
RTTYS_HTTP_PROXY_PORT: ${RTTYS_HTTP_PROXY_PORT:-10443} # addr-http-proxy
# WebRTC / TURN reference (used by rttys template)
TURN_PORT: ${TURN_PORT:-3478}
TURN_USER: ${TURN_USER:-glkvmcloudwebrtcuser}
TURN_PASS: ${TURN_PASS:-AnotherS3cret}
volumes:
- ./templates/rttys.conf.template:/tpl/rttys.conf.tmpl:ro
- ./scripts/docker-entrypoint.sh:/docker-entrypoint.sh:ro
- ./certificate/glkvm.cer:/home/certificate/glkvm_cer:ro
- ./certificate/glkvm.key:/home/certificate/glkvm_key:ro
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
command: ["rttys"]
ports:
- "${RTTYS_WEBUI_PORT:-443}:${RTTYS_WEBUI_PORT:-443}"
- "${RTTYS_HTTP_PROXY_PORT:-10443}:${RTTYS_HTTP_PROXY_PORT:-10443}"
- "${RTTYS_DEVICE_PORT:-5912}:${RTTYS_DEVICE_PORT:-5912}"
coturn:
image: ${COTURN_IMAGE:-coturn/coturn:edge-alpine}
container_name: glkvm_coturn
restart: always
environment:
# Same semantics as above: prefer explicit value, else auto-detect
GLKVM_ACCESS_IP: ${GLKVM_ACCESS_IP:-}
TURN_PORT: ${TURN_PORT:-3478}
TURN_USER: ${TURN_USER:-glkvmcloudwebrtcuser}
TURN_PASS: ${TURN_PASS:-AnotherS3cret}
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
command: ["coturn"]
volumes:
- ./templates/turnserver.conf.template:/tpl/turnserver.conf.tmpl:ro
- ./scripts/docker-entrypoint.sh:/docker-entrypoint.sh:ro
ports:
- "${TURN_PORT:-3478}:3478/tcp"
- "${TURN_PORT:-3478}:3478/udp"
+83
View File
@@ -0,0 +1,83 @@
#!/bin/sh
set -e
# -------- GLKVM access IP resolver (IPv4) --------
resolve_glkvm_access_ip() {
# 1) Prefer user-provided env
if [ -n "${GLKVM_ACCESS_IP:-}" ]; then
echo "${GLKVM_ACCESS_IP}"
return 0
fi
is_ipv4() { echo "$1" | grep -Eq '^[0-9]{1,3}(\.[0-9]{1,3}){3}$'; }
try_cmd() {
val="$("$@" 2>/dev/null | tr -d '\r\n')"
if is_ipv4 "$val"; then
echo "$val"; return 0
fi
return 1
}
# 2) Best-effort auto-detection from multiple sources
if command -v wget >/dev/null 2>&1; then
try_cmd wget -qO- --timeout=3 https://api.ipify.org && return 0
try_cmd wget -qO- --timeout=3 https://ifconfig.me && return 0
fi
if command -v curl >/dev/null 2>&1; then
try_cmd curl -fsS --max-time 3 https://api.ipify.org && return 0
try_cmd curl -fsS --max-time 3 https://ifconfig.me && return 0
fi
if command -v dig >/dev/null 2>&1; then
try_cmd sh -c "dig +short -4 myip.opendns.com @resolver1.opendns.com" && return 0
fi
# 3) Fallback
echo "127.0.0.1"
}
# -------- Minimal template renderer ({{KEY}}) --------
render() {
in="$1"; out="$2"; shift 2
cp "$in" "$out"
for key in "$@"; do
val="$(printenv "$key" || true)"
# Special-case: GLKVM_ACCESS_IP prefers env, else auto-detect once
if [ "$key" = "GLKVM_ACCESS_IP" ] && [ -z "$val" ]; then
val="$(resolve_glkvm_access_ip)"
fi
# Escape '/' and '&' for sed
esc=$(printf '%s' "$val" | sed -e 's/[\/&]/\\&/g')
sed -i "s/{{${key}}}/${esc}/g" "$out"
done
}
# -------- Dispatch by first arg --------
case "$1" in
rttys)
: "${RTTYS_DEVICE_PORT:=5912}"
: "${RTTYS_WEBUI_PORT:=443}"
: "${RTTYS_HTTP_PROXY_PORT:=10443}"
: "${TURN_PORT:=3478}"
render /tpl/rttys.conf.tmpl /home/rttys.conf \
RTTYS_TOKEN RTTYS_PASS \
GLKVM_ACCESS_IP TURN_PORT TURN_USER TURN_PASS \
RTTYS_DEVICE_PORT RTTYS_WEBUI_PORT RTTYS_HTTP_PROXY_PORT
exec rttys -c /home/rttys.conf
;;
coturn)
: "${TURN_PORT:=3478}"
mkdir -p /tmp
render /tpl/turnserver.conf.tmpl /tmp/turnserver.conf \
GLKVM_ACCESS_IP TURN_PORT TURN_USER TURN_PASS
exec turnserver -c /tmp/turnserver.conf
;;
*)
exec "$@"
;;
esac
+16
View File
@@ -0,0 +1,16 @@
# Authentication token for device connections
token: {{RTTYS_TOKEN}}
# Web management password
password: {{RTTYS_PASS}}
# WebRTC
webrtc-ip: {{GLKVM_ACCESS_IP}}
webrtc-port: {{TURN_PORT}}
webrtc-username: {{TURN_USER}}
webrtc-password: {{TURN_PASS}}
# Listen addresses
addr-dev: :{{RTTYS_DEVICE_PORT}}
addr-user: :{{RTTYS_WEBUI_PORT}}
addr-http-proxy: :{{RTTYS_HTTP_PROXY_PORT}}
+7
View File
@@ -0,0 +1,7 @@
listening-port={{TURN_PORT}}
lt-cred-mech
user={{TURN_USER}}:{{TURN_PASS}}
realm=glkvm
no-multicast-peers
allowed-peer-ip=0.0.0.0/0
external-ip={{GLKVM_ACCESS_IP}}