diff --git a/chart/templates/daemonset.yaml b/chart/templates/daemonset.yaml
index 12242b8b40af2af2ee07df92844f910ec18ae8b7..35d5b3519d4540653c799f86cea1bdedf3ac008c 100644
--- a/chart/templates/daemonset.yaml
+++ b/chart/templates/daemonset.yaml
@@ -13,6 +13,13 @@ spec:
     metadata:
       labels:
         {{- include "hcloud-cloud-controller-manager.selectorLabels" . | nindent 8 }}
+        {{- if .Values.podLabels }}
+        {{- toYaml .Values.podLabels | nindent 8 }}
+        {{- end }}
+      {{- if .Values.podAnnotations }}
+      annotations:
+        {{- toYaml .Values.podAnnotations | nindent 8 }}
+      {{- end }}
     spec:
       serviceAccountName: {{ include "hcloud-cloud-controller-manager.name" . }}
       dnsPolicy: Default
diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml
index b59daa2a89b7c5e197e5f7aa86de27f73c6635f7..782a58df4774e55e620b873fbdcce05f0c1c8317 100644
--- a/chart/templates/deployment.yaml
+++ b/chart/templates/deployment.yaml
@@ -14,6 +14,13 @@ spec:
     metadata:
       labels:
         {{- include "hcloud-cloud-controller-manager.selectorLabels" . | nindent 8 }}
+        {{- if .Values.podLabels }}
+        {{- toYaml .Values.podLabels | nindent 8 }}
+        {{- end }}
+      {{- if .Values.podAnnotations }}
+      annotations:
+        {{- toYaml .Values.podAnnotations | nindent 8 }}
+      {{- end }}
     spec:
       serviceAccountName: {{ include "hcloud-cloud-controller-manager.name" . }}
       dnsPolicy: Default
diff --git a/chart/values.yaml b/chart/values.yaml
index 795109d51948bda91698863508ead4046bd6a728..041cb4ea0eba53d895ebec8f7975916172d4dd91 100644
--- a/chart/values.yaml
+++ b/chart/values.yaml
@@ -29,6 +29,16 @@ env:
   # HCLOUD_NETWORK - see networking.enabled
   # ROBOT_ENABLED - see robot.enabled
 
+  # You can also use a file to provide secrets to the hcloud-cloud-controller-manager.
+  # This is currently possible for HCLOUD_TOKEN, ROBOT_USER, and ROBOT_PASSWORD.
+  # Use the env var appended with _FILE (e.g. HCLOUD_TOKEN_FILE) and set the value to the file path that should be read
+  # The file must be provided externally (e.g. via secret injection).
+  # Example:
+  # HCLOUD_TOKEN_FILE:
+  #   value: "/etc/hetzner/token"
+  # to disable reading the token from the secret you have to disable the original env var:
+  # HCLOUD_TOKEN: null
+
   HCLOUD_TOKEN:
     valueFrom:
       secretKeyRef:
@@ -103,3 +113,7 @@ nodeSelector: {}
 robot:
   # Set to true to enable support for Robot (Dedicated) servers.
   enabled: false
+
+podLabels: {}
+
+podAnnotations: {}
diff --git a/internal/config/config.go b/internal/config/config.go
index 9c97e2a6220ff79241c51bf10ad071fe459cf51e..504858a4a9bc4001cdbfb8e7fc728901bc2eb3b2 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"os"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -96,6 +97,32 @@ type HCCMConfiguration struct {
 	Route        RouteConfiguration
 }
 
+// read values from environment variables or from file set via _FILE env var
+// values set directly via env var take precedence over values set via file.
+func readFromEnvOrFile(envVar string) (string, error) {
+	// check if the value is set directly via env (e.g. HCLOUD_TOKEN)
+	value, ok := os.LookupEnv(envVar)
+	if ok {
+		return value, nil
+	}
+
+	// check if the value is set via a file (e.g. HCLOUD_TOKEN_FILE)
+	value, ok = os.LookupEnv(envVar + "_FILE")
+	if !ok {
+		// return no error here, the values could be optional
+		// and the function "Validate()" below checks that all required variables are set
+		return "", nil
+	}
+
+	// read file content
+	valueBytes, err := os.ReadFile(value)
+	if err != nil {
+		return "", fmt.Errorf("failed to read %s: %w", envVar+"_FILE", err)
+	}
+
+	return strings.TrimSpace(string(valueBytes)), nil
+}
+
 // Read evaluates all environment variables and returns a [HCCMConfiguration]. It only validates as far as
 // it needs to parse the values. For business logic validation, check out [HCCMConfiguration.Validate].
 func Read() (HCCMConfiguration, error) {
@@ -106,7 +133,10 @@ func Read() (HCCMConfiguration, error) {
 	var errs []error
 	var cfg HCCMConfiguration
 
-	cfg.HCloudClient.Token = os.Getenv(hcloudToken)
+	cfg.HCloudClient.Token, err = readFromEnvOrFile(hcloudToken)
+	if err != nil {
+		errs = append(errs, err)
+	}
 	cfg.HCloudClient.Endpoint = os.Getenv(hcloudEndpoint)
 	cfg.HCloudClient.Debug, err = getEnvBool(hcloudDebug, false)
 	if err != nil {
@@ -117,8 +147,14 @@ func Read() (HCCMConfiguration, error) {
 	if err != nil {
 		errs = append(errs, err)
 	}
-	cfg.Robot.User = os.Getenv(robotUser)
-	cfg.Robot.Password = os.Getenv(robotPassword)
+	cfg.Robot.User, err = readFromEnvOrFile(robotUser)
+	if err != nil {
+		errs = append(errs, err)
+	}
+	cfg.Robot.Password, err = readFromEnvOrFile(robotPassword)
+	if err != nil {
+		errs = append(errs, err)
+	}
 	cfg.Robot.CacheTimeout, err = getEnvDuration(robotCacheTimeout)
 	if err != nil {
 		errs = append(errs, err)
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 367b640d48ad728c7b3acccef240b0e821320895..ac8d6f9785f99c327c9b9d58acfa7c576ff2ea71 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -14,6 +14,7 @@ func TestRead(t *testing.T) {
 	tests := []struct {
 		name    string
 		env     []string
+		files   map[string]string
 		want    HCCMConfiguration
 		wantErr error
 	}{
@@ -48,6 +49,54 @@ func TestRead(t *testing.T) {
 			},
 			wantErr: nil,
 		},
+		{
+			name: "secrets from file",
+			env: []string{
+				"HCLOUD_TOKEN_FILE", "/tmp/hetzner-token",
+				"ROBOT_USER_FILE", "/tmp/hetzner-user",
+				"ROBOT_PASSWORD_FILE", "/tmp/hetzner-password",
+			},
+			files: map[string]string{
+				"hetzner-token":    "jr5g7ZHpPptyhJzZyHw2Pqu4g9gTqDvEceYpngPf79jN_NOT_VALID_dzhepnahq",
+				"hetzner-user":     "foobar",
+				"hetzner-password": `secret-password`,
+			},
+			want: HCCMConfiguration{
+				HCloudClient: HCloudClientConfiguration{Token: "jr5g7ZHpPptyhJzZyHw2Pqu4g9gTqDvEceYpngPf79jN_NOT_VALID_dzhepnahq"},
+				Robot: RobotConfiguration{
+					Enabled:           false,
+					User:              "foobar",
+					Password:          "secret-password",
+					CacheTimeout:      5 * time.Minute,
+					RateLimitWaitTime: 0,
+				},
+				Metrics:      MetricsConfiguration{Enabled: true, Address: ":8233"},
+				Instance:     InstanceConfiguration{AddressFamily: AddressFamilyIPv4},
+				LoadBalancer: LoadBalancerConfiguration{Enabled: true},
+				Route:        RouteConfiguration{Enabled: false},
+			},
+			wantErr: nil,
+		},
+		{
+			name: "secrets from unknown file",
+			env: []string{
+				"HCLOUD_TOKEN_FILE", "/tmp/hetzner-token",
+				"ROBOT_USER_FILE", "/tmp/hetzner-user",
+				"ROBOT_PASSWORD_FILE", "/tmp/hetzner-password",
+			},
+			files: map[string]string{}, // don't create files
+			want: HCCMConfiguration{
+				HCloudClient: HCloudClientConfiguration{Token: ""},
+				Robot:        RobotConfiguration{User: "", Password: "", CacheTimeout: 0},
+				Metrics:      MetricsConfiguration{Enabled: false},
+				Instance:     InstanceConfiguration{},
+				LoadBalancer: LoadBalancerConfiguration{Enabled: false},
+				Route:        RouteConfiguration{Enabled: false},
+			},
+			wantErr: errors.New(`failed to read HCLOUD_TOKEN_FILE: open /tmp/hetzner-token: no such file or directory
+failed to read ROBOT_USER_FILE: open /tmp/hetzner-user: no such file or directory
+failed to read ROBOT_PASSWORD_FILE: open /tmp/hetzner-password: no such file or directory`),
+		},
 		{
 			name: "client",
 			env: []string{
@@ -207,6 +256,8 @@ failed to parse ROBOT_RATE_LIMIT_WAIT_TIME: time: unknown unit "fortnights" in d
 		t.Run(tt.name, func(t *testing.T) {
 			resetEnv := testsupport.Setenv(t, tt.env...)
 			defer resetEnv()
+			resetFiles := testsupport.SetFiles(t, tt.files)
+			defer resetFiles()
 
 			got, err := Read()
 			if tt.wantErr == nil {
diff --git a/internal/testsupport/files.go b/internal/testsupport/files.go
new file mode 100644
index 0000000000000000000000000000000000000000..1872d73d0f6dcfa14aa96870768f782863d63a07
--- /dev/null
+++ b/internal/testsupport/files.go
@@ -0,0 +1,45 @@
+package testsupport
+
+import (
+	"os"
+	"testing"
+)
+
+// SetFiles can be used to temporarily create files on the local file system.
+// It returns a function that will clean up all files it created.
+func SetFiles(t *testing.T, files map[string]string) func() {
+	for file, content := range files {
+		filepath := os.TempDir() + "/" + file
+
+		// check if file exists
+		_, err := os.Stat(filepath)
+		if err == nil {
+			t.Fatalf("Trying to set file %s, but it already exists. Please choose another filepath for the test.", filepath)
+		}
+
+		// create file
+		f, err := os.Create(filepath)
+		if err != nil {
+			t.Fatalf("Failed to create file %s: %v", filepath, err)
+		}
+
+		// write content to file
+		_, err = f.WriteString(content)
+		if err != nil {
+			t.Fatalf("Failed to write to file %s: %v", filepath, err)
+		}
+
+		// close file
+		f.Close()
+	}
+
+	return func() {
+		for file := range files {
+			filepath := os.TempDir() + "/" + file
+			err := os.Remove(filepath)
+			if err != nil {
+				t.Fatalf("Failed to remove file %s: %v", filepath, err)
+			}
+		}
+	}
+}