mirror of https://github.com/coder/coder.git
feat: Add tunnel by default (#4399)
* feat: Add tunnel by default If an access URL is not specified, we will always tunnel. This is from community-member feedback who exclaimed that it's confusing having the default for `coder server` display a warning message, and I agree. There is very little (maybe none) in running `coder server` without tunnel and without an access URL, so this seems like overall a much better UX. * Update install.sh Co-authored-by: Ben Potter <ben@coder.com> * Update docs/install/packages.md Co-authored-by: Ben Potter <ben@coder.com> * Fix reset pass test * Fix e2e test Co-authored-by: Ben Potter <ben@coder.com>
This commit is contained in:
parent
3049a56355
commit
3cc77d96eb
|
@ -58,7 +58,7 @@ Once installed, you can start a production deployment<sup>1</sup> with a single
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Automatically sets up an external access URL on *.try.coder.app
|
# Automatically sets up an external access URL on *.try.coder.app
|
||||||
coder server --tunnel
|
coder server
|
||||||
|
|
||||||
# Requires a PostgreSQL instance and external access URL
|
# Requires a PostgreSQL instance and external access URL
|
||||||
coder server --postgres-url <url> --access-url <url>
|
coder server --postgres-url <url> --access-url <url>
|
||||||
|
|
|
@ -41,6 +41,7 @@ func TestResetPassword(t *testing.T) {
|
||||||
serverCmd, cfg := clitest.New(t,
|
serverCmd, cfg := clitest.New(t,
|
||||||
"server",
|
"server",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--postgres-url", connectionURL,
|
"--postgres-url", connectionURL,
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -111,7 +111,6 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
tlsEnable bool
|
tlsEnable bool
|
||||||
tlsKeyFiles []string
|
tlsKeyFiles []string
|
||||||
tlsMinVersion string
|
tlsMinVersion string
|
||||||
tunnel bool
|
|
||||||
traceEnable bool
|
traceEnable bool
|
||||||
secureAuthCookie bool
|
secureAuthCookie bool
|
||||||
sshKeygenAlgorithmRaw string
|
sshKeygenAlgorithmRaw string
|
||||||
|
@ -243,26 +242,23 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
if tlsEnable {
|
if tlsEnable {
|
||||||
localURL.Scheme = "https"
|
localURL.Scheme = "https"
|
||||||
}
|
}
|
||||||
if accessURL == "" {
|
|
||||||
accessURL = localURL.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctxTunnel, closeTunnel = context.WithCancel(ctx)
|
ctxTunnel, closeTunnel = context.WithCancel(ctx)
|
||||||
devTunnel *devtunnel.Tunnel
|
tunnel *devtunnel.Tunnel
|
||||||
devTunnelErr <-chan error
|
tunnelErr <-chan error
|
||||||
)
|
)
|
||||||
defer closeTunnel()
|
defer closeTunnel()
|
||||||
|
|
||||||
// If we're attempting to tunnel in dev-mode, the access URL
|
// If the access URL is empty, we attempt to run a reverse-proxy tunnel
|
||||||
// needs to be changed to use the tunnel.
|
// to make the initial setup really simple.
|
||||||
if tunnel {
|
if accessURL == "" {
|
||||||
cmd.Printf("Opening tunnel so workspaces can connect to your deployment\n")
|
cmd.Printf("Opening tunnel so workspaces can connect to your deployment. For production scenarios, specify an external access URL\n")
|
||||||
devTunnel, devTunnelErr, err = devtunnel.New(ctxTunnel, logger.Named("devtunnel"))
|
tunnel, tunnelErr, err = devtunnel.New(ctxTunnel, logger.Named("devtunnel"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("create tunnel: %w", err)
|
return xerrors.Errorf("create tunnel: %w", err)
|
||||||
}
|
}
|
||||||
accessURL = devTunnel.URL
|
accessURL = tunnel.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
accessURLParsed, err := parseURL(ctx, accessURL)
|
accessURLParsed, err := parseURL(ctx, accessURL)
|
||||||
|
@ -288,11 +284,11 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
if isLocal {
|
if isLocal {
|
||||||
reason = "isn't externally reachable"
|
reason = "isn't externally reachable"
|
||||||
}
|
}
|
||||||
cmd.Printf("%s The access URL %s %s, this may cause unexpected problems when creating workspaces. Generate a unique *.try.coder.app URL with:\n", cliui.Styles.Warn.Render("Warning:"), cliui.Styles.Field.Render(accessURLParsed.String()), reason)
|
cmd.Printf("%s The access URL %s %s, this may cause unexpected problems when creating workspaces. Generate a unique *.try.coder.app URL by not specifying an access URL.\n", cliui.Styles.Warn.Render("Warning:"), cliui.Styles.Field.Render(accessURLParsed.String()), reason)
|
||||||
cmd.Println(cliui.Styles.Code.Render(strings.Join(os.Args, " ") + " --tunnel"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Printf("View the Web UI: %s\n", accessURLParsed.String())
|
// A newline is added before for visibility in terminal output.
|
||||||
|
cmd.Printf("\nView the Web UI: %s\n", accessURLParsed.String())
|
||||||
|
|
||||||
// Used for zero-trust instance identity with Google Cloud.
|
// Used for zero-trust instance identity with Google Cloud.
|
||||||
googleTokenValidator, err := idtoken.NewValidator(ctx, option.WithoutAuthentication())
|
googleTokenValidator, err := idtoken.NewValidator(ctx, option.WithoutAuthentication())
|
||||||
|
@ -472,7 +468,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
OIDCIssuerURL: oidcIssuerURL,
|
OIDCIssuerURL: oidcIssuerURL,
|
||||||
Prometheus: promEnabled,
|
Prometheus: promEnabled,
|
||||||
STUN: len(derpServerSTUNAddrs) != 0,
|
STUN: len(derpServerSTUNAddrs) != 0,
|
||||||
Tunnel: tunnel,
|
Tunnel: tunnel != nil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("create telemetry reporter: %w", err)
|
return xerrors.Errorf("create telemetry reporter: %w", err)
|
||||||
|
@ -569,17 +565,17 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
// Make sure to close the tunnel listener if we exit so the
|
// Make sure to close the tunnel listener if we exit so the
|
||||||
// errgroup doesn't wait forever!
|
// errgroup doesn't wait forever!
|
||||||
if tunnel {
|
if tunnel != nil {
|
||||||
defer devTunnel.Listener.Close()
|
defer tunnel.Listener.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return server.Serve(listener)
|
return server.Serve(listener)
|
||||||
})
|
})
|
||||||
if tunnel {
|
if tunnel != nil {
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
|
|
||||||
return server.Serve(devTunnel.Listener)
|
return server.Serve(tunnel.Listener)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -624,7 +620,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Bold.Render(
|
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Bold.Render(
|
||||||
"Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit",
|
"Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit",
|
||||||
))
|
))
|
||||||
case exitErr = <-devTunnelErr:
|
case exitErr = <-tunnelErr:
|
||||||
if exitErr == nil {
|
if exitErr == nil {
|
||||||
exitErr = xerrors.New("dev tunnel closed unexpectedly")
|
exitErr = xerrors.New("dev tunnel closed unexpectedly")
|
||||||
}
|
}
|
||||||
|
@ -650,9 +646,9 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
// in-flight requests, give in-flight requests 5 seconds to
|
// in-flight requests, give in-flight requests 5 seconds to
|
||||||
// complete.
|
// complete.
|
||||||
cmd.Println("Shutting down API server...")
|
cmd.Println("Shutting down API server...")
|
||||||
err = shutdownWithTimeout(server.Shutdown, 5*time.Second)
|
err = shutdownWithTimeout(server.Shutdown, 3*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.Printf("API server shutdown took longer than 5s: %s", err)
|
cmd.Printf("API server shutdown took longer than 3s: %s\n", err)
|
||||||
} else {
|
} else {
|
||||||
cmd.Printf("Gracefully shut down API server\n")
|
cmd.Printf("Gracefully shut down API server\n")
|
||||||
}
|
}
|
||||||
|
@ -694,10 +690,10 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
cmd.Println("Done waiting for WebSocket connections")
|
cmd.Println("Done waiting for WebSocket connections")
|
||||||
|
|
||||||
// Close tunnel after we no longer have in-flight connections.
|
// Close tunnel after we no longer have in-flight connections.
|
||||||
if tunnel {
|
if tunnel != nil {
|
||||||
cmd.Println("Waiting for tunnel to close...")
|
cmd.Println("Waiting for tunnel to close...")
|
||||||
closeTunnel()
|
closeTunnel()
|
||||||
<-devTunnelErr
|
<-tunnelErr
|
||||||
cmd.Println("Done waiting for tunnel")
|
cmd.Println("Done waiting for tunnel")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,8 +851,6 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
|
||||||
"Paths to the private keys for each of the certificates. It requires a PEM-encoded file")
|
"Paths to the private keys for each of the certificates. It requires a PEM-encoded file")
|
||||||
cliflag.StringVarP(root.Flags(), &tlsMinVersion, "tls-min-version", "", "CODER_TLS_MIN_VERSION", "tls12",
|
cliflag.StringVarP(root.Flags(), &tlsMinVersion, "tls-min-version", "", "CODER_TLS_MIN_VERSION", "tls12",
|
||||||
`Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`)
|
`Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`)
|
||||||
cliflag.BoolVarP(root.Flags(), &tunnel, "tunnel", "", "CODER_TUNNEL", false,
|
|
||||||
"Workspaces must be able to reach the `access-url`. This overrides your access URL with a public access URL that tunnels your Coder deployment.")
|
|
||||||
cliflag.BoolVarP(root.Flags(), &traceEnable, "trace", "", "CODER_TRACE", false,
|
cliflag.BoolVarP(root.Flags(), &traceEnable, "trace", "", "CODER_TRACE", false,
|
||||||
"Whether application tracing data is collected.")
|
"Whether application tracing data is collected.")
|
||||||
cliflag.BoolVarP(root.Flags(), &secureAuthCookie, "secure-auth-cookie", "", "CODER_SECURE_AUTH_COOKIE", false,
|
cliflag.BoolVarP(root.Flags(), &secureAuthCookie, "secure-auth-cookie", "", "CODER_SECURE_AUTH_COOKIE", false,
|
||||||
|
|
|
@ -56,6 +56,7 @@ func TestServer(t *testing.T) {
|
||||||
root, cfg := clitest.New(t,
|
root, cfg := clitest.New(t,
|
||||||
"server",
|
"server",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--postgres-url", connectionURL,
|
"--postgres-url", connectionURL,
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
|
@ -87,6 +88,7 @@ func TestServer(t *testing.T) {
|
||||||
root, cfg := clitest.New(t,
|
root, cfg := clitest.New(t,
|
||||||
"server",
|
"server",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
pty := ptytest.New(t)
|
pty := ptytest.New(t)
|
||||||
|
@ -159,6 +161,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--access-url", "foobarbaz.mydomain",
|
"--access-url", "foobarbaz.mydomain",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
|
@ -189,6 +192,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--access-url", "https://google.com",
|
"--access-url", "https://google.com",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
|
@ -218,6 +222,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--tls-enable",
|
"--tls-enable",
|
||||||
"--tls-min-version", "tls9",
|
"--tls-min-version", "tls9",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
|
@ -234,6 +239,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--tls-enable",
|
"--tls-enable",
|
||||||
"--tls-client-auth", "something",
|
"--tls-client-auth", "something",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
|
@ -290,6 +296,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
}
|
}
|
||||||
args = append(args, c.args...)
|
args = append(args, c.args...)
|
||||||
|
@ -310,6 +317,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--tls-enable",
|
"--tls-enable",
|
||||||
"--tls-cert-file", certPath,
|
"--tls-cert-file", certPath,
|
||||||
"--tls-key-file", keyPath,
|
"--tls-key-file", keyPath,
|
||||||
|
@ -349,6 +357,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--tls-enable",
|
"--tls-enable",
|
||||||
"--tls-cert-file", cert1Path,
|
"--tls-cert-file", cert1Path,
|
||||||
"--tls-key-file", key1Path,
|
"--tls-key-file", key1Path,
|
||||||
|
@ -432,6 +441,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--provisioner-daemons", "1",
|
"--provisioner-daemons", "1",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
|
@ -458,6 +468,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--trace=true",
|
"--trace=true",
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
)
|
)
|
||||||
|
@ -495,6 +506,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--telemetry",
|
"--telemetry",
|
||||||
"--telemetry-url", server.URL,
|
"--telemetry-url", server.URL,
|
||||||
"--cache-dir", t.TempDir(),
|
"--cache-dir", t.TempDir(),
|
||||||
|
@ -525,6 +537,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--provisioner-daemons", "1",
|
"--provisioner-daemons", "1",
|
||||||
"--prometheus-enable",
|
"--prometheus-enable",
|
||||||
"--prometheus-address", ":"+strconv.Itoa(randomPort),
|
"--prometheus-address", ":"+strconv.Itoa(randomPort),
|
||||||
|
@ -577,6 +590,7 @@ func TestServer(t *testing.T) {
|
||||||
"server",
|
"server",
|
||||||
"--in-memory",
|
"--in-memory",
|
||||||
"--address", ":0",
|
"--address", ":0",
|
||||||
|
"--access-url", "example.com",
|
||||||
"--oauth2-github-client-id", "fake",
|
"--oauth2-github-client-id", "fake",
|
||||||
"--oauth2-github-client-secret", "fake",
|
"--oauth2-github-client-secret", "fake",
|
||||||
"--oauth2-github-enterprise-base-url", fakeRedirect,
|
"--oauth2-github-enterprise-base-url", fakeRedirect,
|
||||||
|
|
|
@ -1,11 +1,4 @@
|
||||||
# Coder must be reachable from an external URL
|
# Coder must be reachable from an external URL for users and workspaces to connect.
|
||||||
# for users and workspaces to connect.
|
|
||||||
|
|
||||||
# Option 1) Enable CODER_TUNNEL to generate a
|
|
||||||
# unique *. try.coder.com access URL
|
|
||||||
CODER_TUNNEL=false
|
|
||||||
|
|
||||||
# Option 2) Set an access URL
|
|
||||||
# e.g. https://coder.example.com
|
# e.g. https://coder.example.com
|
||||||
CODER_ACCESS_URL=
|
CODER_ACCESS_URL=
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (api *API) checkAuthorization(rw http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
api.Logger.Warn(ctx, "check-auth",
|
api.Logger.Debug(ctx, "check-auth",
|
||||||
slog.F("my_id", httpmw.APIKey(r).UserID),
|
slog.F("my_id", httpmw.APIKey(r).UserID),
|
||||||
slog.F("got_id", auth.ID),
|
slog.F("got_id", auth.ID),
|
||||||
slog.F("name", auth.Username),
|
slog.F("name", auth.Username),
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (api *API) logReportCSPViolations(rw http.ResponseWriter, r *http.Request)
|
||||||
for k, v := range v.Report {
|
for k, v := range v.Report {
|
||||||
fields = append(fields, slog.F(k, v))
|
fields = append(fields, slog.F(k, v))
|
||||||
}
|
}
|
||||||
api.Logger.Warn(ctx, "csp violation", fields...)
|
api.Logger.Debug(ctx, "csp violation", fields...)
|
||||||
|
|
||||||
httpapi.Write(ctx, rw, http.StatusOK, "ok")
|
httpapi.Write(ctx, rw, http.StatusOK, "ok")
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ func (api *API) dialWorkspaceAgentTailnet(r *http.Request, agentID uuid.UUID) (*
|
||||||
conn, err := tailnet.NewConn(&tailnet.Options{
|
conn, err := tailnet.NewConn(&tailnet.Options{
|
||||||
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
|
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
|
||||||
DERPMap: derpMap,
|
DERPMap: derpMap,
|
||||||
Logger: api.Logger.Named("tailnet").Leveled(slog.LevelDebug),
|
Logger: api.Logger.Named("tailnet"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("create tailnet conn: %w", err)
|
return nil, xerrors.Errorf("create tailnet conn: %w", err)
|
||||||
|
@ -453,7 +453,7 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
|
||||||
// Ignore all trace spans after this.
|
// Ignore all trace spans after this.
|
||||||
ctx = trace.ContextWithSpan(ctx, tracing.NoopSpan)
|
ctx = trace.ContextWithSpan(ctx, tracing.NoopSpan)
|
||||||
|
|
||||||
api.Logger.Info(ctx, "accepting agent", slog.F("resource", resource), slog.F("agent", workspaceAgent))
|
api.Logger.Info(ctx, "accepting agent", slog.F("agent", workspaceAgent))
|
||||||
|
|
||||||
defer conn.Close(websocket.StatusNormalClosure, "")
|
defer conn.Close(websocket.StatusNormalClosure, "")
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,6 @@ services:
|
||||||
# that workspaces can reach. This cannot be localhost
|
# that workspaces can reach. This cannot be localhost
|
||||||
# or 127.0.0.1 for non-Docker templates!
|
# or 127.0.0.1 for non-Docker templates!
|
||||||
CODER_ACCESS_URL: "${CODER_ACCESS_URL}"
|
CODER_ACCESS_URL: "${CODER_ACCESS_URL}"
|
||||||
# Alternatively, you can enable CODER_TUNNEL for
|
|
||||||
# proof-of-concept deployments.
|
|
||||||
CODER_TUNNEL: "${CODER_TUNNEL:-false}"
|
|
||||||
# If the coder user does not have write permissions on
|
# If the coder user does not have write permissions on
|
||||||
# the docker socket, you can uncomment the following
|
# the docker socket, you can uncomment the following
|
||||||
# lines and set the group ID to one that has write
|
# lines and set the group ID to one that has write
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
Coder server's primary configuration is done via environment variables. For a full list
|
Coder server's primary configuration is done via environment variables. For a full list
|
||||||
of the options, run `coder server --help` on the host.
|
of the options, run `coder server --help` on the host.
|
||||||
|
|
||||||
## Tunnel
|
|
||||||
|
|
||||||
For proof-of-concept deployments, you can set `CODER_TUNNEL=true` to run Coder on a unique `*.try.coder.app` URL.
|
|
||||||
This is a quick way to allow users and workspaces outside your LAN to connect to Coder.
|
|
||||||
|
|
||||||
## Access URL
|
## Access URL
|
||||||
|
|
||||||
`CODER_ACCESS_URL` is required if you are not using the tunnel. Set this to the external URL
|
`CODER_ACCESS_URL` is required if you are not using the tunnel. Set this to the external URL
|
||||||
|
@ -14,6 +9,11 @@ should not be localhost.
|
||||||
|
|
||||||
> Access URL should be a external IP address or domain with DNS records pointing to Coder.
|
> Access URL should be a external IP address or domain with DNS records pointing to Coder.
|
||||||
|
|
||||||
|
### Tunnel
|
||||||
|
|
||||||
|
If an access URL is not specified, Coder will create
|
||||||
|
a publicly accessible URL to reverse proxy your deployment for simple setup.
|
||||||
|
|
||||||
## Wildcard access URL
|
## Wildcard access URL
|
||||||
|
|
||||||
`CODER_WILDCARD_ACCESS_URL` is necessary for [port forwarding](../networking/port-forwarding.md#dashboard)
|
`CODER_WILDCARD_ACCESS_URL` is necessary for [port forwarding](../networking/port-forwarding.md#dashboard)
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 53 KiB |
Binary file not shown.
Before Width: | Height: | Size: 53 KiB |
|
@ -15,7 +15,7 @@ Coder publishes self-contained .zip and .tar.gz archives in [GitHub releases](ht
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Automatically sets up an external access URL on *.try.coder.app
|
# Automatically sets up an external access URL on *.try.coder.app
|
||||||
coder server --tunnel
|
coder server
|
||||||
|
|
||||||
# Requires a PostgreSQL instance and external access URL
|
# Requires a PostgreSQL instance and external access URL
|
||||||
coder server --postgres-url <url> --access-url <url>
|
coder server --postgres-url <url> --access-url <url>
|
||||||
|
|
|
@ -4,7 +4,7 @@ You can install and run Coder using the official Docker images published on [Git
|
||||||
|
|
||||||
Docker is required. See the [official installation documentation](https://docs.docker.com/install/).
|
Docker is required. See the [official installation documentation](https://docs.docker.com/install/).
|
||||||
|
|
||||||
## Run Coder with built-in database and tunnel (quick)
|
## Run Coder with the built-in database (quick)
|
||||||
|
|
||||||
For proof-of-concept deployments, you can run a complete Coder instance with
|
For proof-of-concept deployments, you can run a complete Coder instance with
|
||||||
the following command:
|
the following command:
|
||||||
|
@ -14,7 +14,6 @@ export CODER_DATA=$HOME/.config/coderv2-docker
|
||||||
export DOCKER_GROUP=$(getent group docker | cut -d: -f3)
|
export DOCKER_GROUP=$(getent group docker | cut -d: -f3)
|
||||||
mkdir -p $CODER_DATA
|
mkdir -p $CODER_DATA
|
||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
-e CODER_TUNNEL=true \
|
|
||||||
-v $CODER_DATA:/home/coder/.config \
|
-v $CODER_DATA:/home/coder/.config \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
--group-add $DOCKER_GROUP \
|
--group-add $DOCKER_GROUP \
|
||||||
|
@ -68,7 +67,7 @@ an PostgreSQL container and volume.
|
||||||
```sh
|
```sh
|
||||||
cd coder
|
cd coder
|
||||||
|
|
||||||
CODER_TUNNEL=true docker-compose up
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
For production deployments, we recommend setting an [access URL](../admin/configure.md#access-url):
|
For production deployments, we recommend setting an [access URL](../admin/configure.md#access-url):
|
||||||
|
@ -79,8 +78,6 @@ an PostgreSQL container and volume.
|
||||||
CODER_ACCESS_URL=https://coder.example.com docker-compose up
|
CODER_ACCESS_URL=https://coder.example.com docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
> Without `CODER_ACCESS_URL` or `CODER_TUNNEL` set, Coder will bind to `localhost:7080`. This will only work for Docker-based templates.
|
|
||||||
|
|
||||||
4. Visit the web ui via the configured url. You can add `/login` to the base url to create the first user via the ui.
|
4. Visit the web ui via the configured url. You can add `/login` to the base url to create the first user via the ui.
|
||||||
|
|
||||||
5. Follow the on-screen instructions log in and create your first template and workspace
|
5. Follow the on-screen instructions log in and create your first template and workspace
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
1. Run Coder as a system service.
|
1. Run Coder as a system service.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Set up an access URL or enable CODER_TUNNEL
|
# Optional) Set up an access URL
|
||||||
sudo vim /etc/coder.d/coder.env
|
sudo vim /etc/coder.d/coder.env
|
||||||
|
|
||||||
# To systemd to start Coder now and on reboot
|
# To systemd to start Coder now and on reboot
|
||||||
|
|
|
@ -12,8 +12,6 @@ Please [install Coder](../install) before proceeding with the steps below.
|
||||||
1. Run `coder login <your Access URL>` in a new terminal and follow the
|
1. Run `coder login <your Access URL>` in a new terminal and follow the
|
||||||
interactive instructions to create your owner user and password.
|
interactive instructions to create your owner user and password.
|
||||||
|
|
||||||
> If using `coder server --tunnel`, the Access URL appears in the terminal logs.
|
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
To get started using templates, run the following command to generate a sample template:
|
To get started using templates, run the following command to generate a sample template:
|
||||||
|
|
|
@ -78,15 +78,7 @@ curl -fsSL https://coder.com/install.sh | sh
|
||||||
|
|
||||||
## Run Coder
|
## Run Coder
|
||||||
|
|
||||||
First, edit the `coder.env` file to enable `CODER_TUNNEL` by setting the value to true with the following command:
|
Run the following command to start Coder as a system level service:
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo vim /etc/coder.d/coder.env
|
|
||||||
```
|
|
||||||
|
|
||||||
<img src="../images/quickstart/aws/aws7.png">
|
|
||||||
|
|
||||||
Exit vim and run the following command to start Coder as a system level service:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo systemctl enable --now coder
|
sudo systemctl enable --now coder
|
||||||
|
|
|
@ -49,15 +49,7 @@ curl -fsSL <https://coder.com/install.sh> | sh
|
||||||
|
|
||||||
## Run Coder
|
## Run Coder
|
||||||
|
|
||||||
First, edit the `coder.env` file to enable `CODER_TUNNEL` by setting the value to true with the following command:
|
Run the following command to start Coder as a system level service:
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo vim /etc/coder.d/coder.env
|
|
||||||
```
|
|
||||||
|
|
||||||
<img src="../images/quickstart/azure/azure7.png">
|
|
||||||
|
|
||||||
Exit vim and run the following command to start Coder as a system level service:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo systemctl enable --now coder
|
sudo systemctl enable --now coder
|
||||||
|
|
|
@ -15,19 +15,13 @@ Coder with Docker has the following advantages:
|
||||||
|
|
||||||
1. [Install and launch Coder](../install)
|
1. [Install and launch Coder](../install)
|
||||||
|
|
||||||
Next, we export the `CODER_ADDRESS` and `CODER_ACCESS_URL` environment
|
The Coder server binds to port 3000 by default. Use `--address :<port>` to customize it!
|
||||||
variables. We can use localhost for the Access URL since the workspaces
|
|
||||||
all run on the same machine. `CODER_ADDRESS` is where coder server binds
|
|
||||||
while `CODER_ACCESS_URL` is where it's accessed. We use `:7080` to bind
|
|
||||||
to all interfaces.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ export CODER_ADDRESS=:7080
|
$ coder server
|
||||||
$ export CODER_ACCESS_URL=http://localhost:7080
|
|
||||||
$ coder server --address $CODER_ADDRESS --access-url $CODER_ACCESS_URL
|
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Run `coder login http://localhost:7080` in a new terminal and follow the
|
1. Run `coder login http://localhost:3000` in a new terminal and follow the
|
||||||
interactive instructions to create your user.
|
interactive instructions to create your user.
|
||||||
|
|
||||||
1. Pull the "Docker" example template using the interactive `coder templates init`:
|
1. Pull the "Docker" example template using the interactive `coder templates init`:
|
||||||
|
@ -38,7 +32,7 @@ Coder with Docker has the following advantages:
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Push up the template with `coder templates create`
|
1. Push up the template with `coder templates create`
|
||||||
1. Open the dashboard in your browser (http://localhost:7080) to create your
|
1. Open the dashboard in your browser (http://localhost:3000) to create your
|
||||||
first workspace:
|
first workspace:
|
||||||
|
|
||||||
<img src="../images/quickstart/docker/login.png">
|
<img src="../images/quickstart/docker/login.png">
|
||||||
|
|
|
@ -121,7 +121,7 @@ $1 package has been installed.
|
||||||
|
|
||||||
To run Coder as a system service:
|
To run Coder as a system service:
|
||||||
|
|
||||||
# Set up an external access URL or enable CODER_TUNNEL
|
# Optional) Set up an external access URL
|
||||||
$ sudo vim /etc/coder.d/coder.env
|
$ sudo vim /etc/coder.d/coder.env
|
||||||
# Use systemd to start Coder now and on reboot
|
# Use systemd to start Coder now and on reboot
|
||||||
$ sudo systemctl enable --now coder
|
$ sudo systemctl enable --now coder
|
||||||
|
|
|
@ -59,7 +59,7 @@ CODER_DEV_SHIM="${PROJECT_ROOT}/scripts/coder-dev.sh"
|
||||||
# rather than leaving things in an inconsistent state.
|
# rather than leaving things in an inconsistent state.
|
||||||
trap 'kill -TERM -$$' ERR
|
trap 'kill -TERM -$$' ERR
|
||||||
cdroot
|
cdroot
|
||||||
"${CODER_DEV_SHIM}" server --address 0.0.0.0:3000 --tunnel || kill -INT -$$ &
|
"${CODER_DEV_SHIM}" server --address 0.0.0.0:3000 || kill -INT -$$ &
|
||||||
|
|
||||||
echo '== Waiting for Coder to become ready'
|
echo '== Waiting for Coder to become ready'
|
||||||
timeout 60s bash -c 'until curl -s --fail http://localhost:3000 > /dev/null 2>&1; do sleep 0.5; done'
|
timeout 60s bash -c 'until curl -s --fail http://localhost:3000 > /dev/null 2>&1; do sleep 0.5; done'
|
||||||
|
|
|
@ -24,7 +24,7 @@ const config: PlaywrightTestConfig = {
|
||||||
command: `go run -tags embed ${path.join(
|
command: `go run -tags embed ${path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
"../../enterprise/cmd/coder/main.go",
|
"../../enterprise/cmd/coder/main.go",
|
||||||
)} server --in-memory`,
|
)} server --in-memory --access-url 127.0.0.1:${basePort}`,
|
||||||
port: basePort,
|
port: basePort,
|
||||||
timeout: 120 * 10000,
|
timeout: 120 * 10000,
|
||||||
reuseExistingServer: false,
|
reuseExistingServer: false,
|
||||||
|
|
Loading…
Reference in New Issue