powerful but friendly backup program that runs in your tray, powered by restic devins.page/restray
go restic system-tray
2

Configure Feed

Select the types of activity you want to include in your feed.

refactor: clean up systemd service and behavior of `--system`

intergrav (Jul 9, 2026, 9:03 AM EDT) b5904bf3 59d84f38

+55 -16
+3 -1
README.md
··· 48 48 49 49 Restray also works without the GUI on MacOS/Linux. Run `restray daemon` to run just the built-in scheduler (no tray GUI), or use one-shot commands for other things. Run `restray --help` for the full command list. 50 50 51 - Linux users: systemd unit files are included (`packaging/linux/restray.service` for system-wide, `packaging/linux/restray-user.service` for per-user). If you use NixOS, a `services.restray` module is also exported from the flake :) 51 + Linux users/packagers: systemd unit files are included (`packaging/linux/restray.service` for system-wide, `packaging/linux/restray-user.service` for per-user). If you use NixOS, a `services.restray` module is also exported from the flake :) 52 + 53 + For Linux system-wide setups, you can run commands against the same config/state as the system service with `restray --system ...`, or explicitly with `restray --config /etc/restray --state /var/lib/restray ...`. This is useful for manually running operations using the system-wide config. You will need to have elevated priviledges though (run with sudo) 52 54 53 55 ## Configuration 54 56
+50 -9
cmd/restray/main.go
··· 5 5 "log" 6 6 "os" 7 7 "runtime" 8 + "strings" 8 9 9 10 "fyne.io/systray" 10 11 "github.com/gen2brain/beeep" ··· 17 18 systemStateDir = "/var/lib/restray" 18 19 ) 19 20 21 + func applyRuntimeContext(configDir, stateDir string, setHome bool) { 22 + if configDir != "" { 23 + configDirOverride = configDir 24 + } 25 + if stateDir != "" { 26 + dataDirOverride = stateDir 27 + } 28 + if setHome && dataDirOverride != "" { 29 + os.Setenv("HOME", dataDirOverride) 30 + } 31 + } 32 + 33 + func parsePathFlag(args []string, i *int, name string) string { 34 + if value, ok := strings.CutPrefix(args[*i], name+"="); ok { 35 + if value == "" { 36 + log.Fatalf("restray: %s requires a non-empty value", name) 37 + } 38 + return value 39 + } 40 + if *i+1 >= len(args) { 41 + log.Fatalf("restray: %s requires a value", name) 42 + } 43 + *i++ 44 + return args[*i] 45 + } 46 + 20 47 func preParseGlobalFlags() (verbose bool, remaining []string) { 21 48 args := os.Args[1:] 49 + var system bool 22 50 for i := 0; i < len(args); i++ { 23 - switch args[i] { 51 + arg := args[i] 52 + switch arg { 24 53 case "-v", "--verbose": 25 54 verbose = true 26 - case "-c", "--config": 27 - if i+1 < len(args) { 28 - configDirOverride = args[i+1] 29 - i++ 30 - } 31 55 case "--system": 32 56 if runtime.GOOS != "linux" { 33 57 log.Fatalf("restray: --system is not supported on %s", runtime.GOOS) 34 58 } 35 - configDirOverride = systemConfigDir 36 - dataDirOverride = systemStateDir 59 + system = true 37 60 default: 38 - remaining = append(remaining, args[i]) 61 + switch { 62 + case arg == "-c" || arg == "--config" || strings.HasPrefix(arg, "--config="): 63 + applyRuntimeContext(parsePathFlag(args, &i, "--config"), "", false) 64 + case arg == "--state" || strings.HasPrefix(arg, "--state="): 65 + applyRuntimeContext("", parsePathFlag(args, &i, "--state"), false) 66 + default: 67 + remaining = append(remaining, arg) 68 + } 39 69 } 70 + } 71 + if system { 72 + configDir := configDirOverride 73 + if configDir == "" { 74 + configDir = systemConfigDir 75 + } 76 + stateDir := dataDirOverride 77 + if stateDir == "" { 78 + stateDir = systemStateDir 79 + } 80 + applyRuntimeContext(configDir, stateDir, true) 40 81 } 41 82 return verbose, remaining 42 83 }
+1 -3
nix/nixos-module.nix
··· 37 37 wantedBy = ["multi-user.target"]; 38 38 serviceConfig = { 39 39 Type = "simple"; 40 - ExecStart = "${lib.getExe cfg.package} daemon"; 40 + ExecStart = "${lib.getExe cfg.package} --config /etc/restray --state /var/lib/restray daemon"; 41 41 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 42 42 Restart = "on-failure"; 43 43 StateDirectory = "restray"; 44 44 Environment = [ 45 - "RESTRAY_CONFIG=/etc/restray" 46 - "RESTRAY_STATE=/var/lib/restray" 47 45 "HOME=/var/lib/restray" 48 46 ]; 49 47 };
+1 -3
packaging/linux/restray.service
··· 3 3 4 4 [Service] 5 5 Type=simple 6 - Environment=RESTRAY_CONFIG=/etc/restray 7 6 StateDirectory=restray 8 - Environment=RESTRAY_STATE=/var/lib/restray 9 7 Environment=HOME=/var/lib/restray 10 - ExecStart=restray daemon 8 + ExecStart=restray --config /etc/restray --state /var/lib/restray daemon 11 9 ExecReload=/bin/kill -HUP $MAINPID 12 10 Restart=on-failure 13 11