# Install script (/en/docs/install/script)

Download a release on Linux or macOS, verify its sha256 and put it on the PATH; on Linux it can also register a systemd service. Running it again upgrades.

## Usage [#usage]

```bash title="Terminal"
curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sh
```

Pass options to the script through `sh -s --`:

```bash title="Terminal"
# Install as a systemd service and start it (Linux only, needs root)
curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sudo sh -s -- --service

# Install a specific version
curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sh -s -- --version 1.0.0
```

To read the script before running it:

```bash title="Terminal"
curl -fsSLo install.sh https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh
less install.sh
sudo sh install.sh --service
```

The script needs `curl`, `tar`, `install`, and either `sha256sum` or `shasum`; `--service` also needs `systemctl`.

## Options and environment variables [#options-and-environment-variables]

| Option | Default | Description |
|---|---|---|
| `--version X.Y.Z` | Latest stable release | Install a specific version; the `v` prefix is optional |
| `--dir DIR` | `/usr/local/bin` for root, `~/.local/bin` for other users | Where to install the binary; created if missing, must be writable. With `--service` it must be an absolute path outside `/home`, `/root` and `/run/user` |
| `--service` | Not installed | Linux only, needs root: also install and start the systemd service `imgyoung`, with data in `/var/lib/imgyoung` |

| Environment variable | Default | Description |
|---|---|---|
| `IMGYOUNG_RELEASES` | `https://github.com/x-haose/imgyoung/releases` | The releases address. Set it to use a mirror or internal distribution; the layout must match GitHub Releases (see "Mirrors and internal distribution" below) |

An unknown option or a malformed version makes the script exit with an error right away, before it changes anything.

## What the script does [#what-the-script-does]

1. **Checks prerequisites.** Confirms `curl`, `tar` and `install` exist; with `--service` it also checks for Linux, root, `systemctl`, and that any `--dir` given is an absolute path outside the home directories. If anything is missing it exits before downloading and leaves the system untouched.
2. **Detects the platform.** `uname -s` must be Linux or Darwin, `uname -m` must be x86_64 / amd64 or aarch64 / arm64; anything else is an error (on Windows, use a [release archive](./binary.mdx)).
3. **Picks the version.** Without `--version` it requests `<releases>/latest`, follows the redirect and takes the last path segment of the final URL as the tag. GitHub's latest release never points at a pre-release.
4. **Downloads and verifies.** Downloads `imgyoung_<version>_<os>_<arch>.tar.gz` and that release's `checksums.txt` into a temporary directory and checks the sha256. On a mismatch it aborts without installing anything. The temporary directory is removed when the script exits.
5. **Installs the binary.** Installs `imgyoung` into the target directory with mode 0755 and prints its version.
6. **Installs the service** (`--service` only). Writes the archive's `deploy/imgyoung.service` to `/etc/systemd/system/imgyoung.service` with `ExecStart` pointing at the actual install path, saying so if the content differs from the existing file, then runs `systemctl daemon-reload` and enables it. If the service is already running it is restarted onto the new version; otherwise it is started.
7. **Prints next steps.** Warns if the target directory is not on `PATH`, and prints the sign-in address and factory account.

Without `--service` the script only installs the binary and does not start it. Run `imgyoung` yourself; data goes to `data/` under the current directory (or `IMGYOUNG_DATA_DIR`). For long-running use, put it under a process supervisor; see [Installation](./index.mdx) for why.

## The systemd service [#the-systemd-service]

`--service` installs this sample from the repository, changing only `ExecStart`:

```ini title="deploy/imgyoung.service"
# ImgYoung 的 systemd 服务示例;install.sh --service 安装的就是它。
# 手动安装:二进制放到 /usr/local/bin/imgyoung,本文件复制到 /etc/systemd/system/imgyoung.service,
# 然后 systemctl daemon-reload && systemctl enable --now imgyoung。
# 数据在 /var/lib/imgyoung(数据库、本地存储渠道、master.key),备份它。
[Unit]
Description=ImgYoung 自部署图床
Documentation=https://github.com/x-haose/imgyoung
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/imgyoung
Environment=IMGYOUNG_DATA_DIR=%S/imgyoung
# 渠道参数直接交给 rclone,不用 rclone 的配置文件;置空让 rclone 启动时不去寻找家目录下的配置
Environment=RCLONE_CONFIG=
# 只经本机反向代理对外时,改成只监听回环:
# Environment=IMGYOUNG_LISTEN=127.0.0.1:8721

# 以临时系统用户运行,数据目录由 systemd 创建并归属该用户
DynamicUser=yes
StateDirectory=imgyoung

# 后台「从快照恢复」会让服务优雅退出,靠这里重新拉起后换入快照;正常退出也要拉起
Restart=always
RestartSec=3s
# 优雅停机先停收新请求、等在途请求至多 6 秒,再停后台任务至多 2 秒;多出的是关库与退出的余量
TimeoutStopSec=15s

# 数据目录里有 master.key,新建文件只给运行用户自己读写
UMask=0077
# 进程只能写自己的数据目录;本地存储渠道指向别处时,用 ReadWritePaths= 放开那个目录
NoNewPrivileges=yes
CapabilityBoundingSet=
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectClock=yes
ProtectHostname=yes
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
LockPersonality=yes
SystemCallArchitectures=native

[Install]
WantedBy=multi-user.target
```

The settings that matter:

- **Data directory.** `IMGYOUNG_DATA_DIR=%S/imgyoung` resolves to `/var/lib/imgyoung`, which `StateDirectory=imgyoung` makes systemd create and hand to the service user. With `DynamicUser` the real directory is `/var/lib/private/imgyoung` and `/var/lib/imgyoung` is a symlink to it. It holds `imgyoung.db`, `master.key`, the local channel's `blobs/` and the variant cache `variants/`.
- **DynamicUser=yes.** systemd allocates a transient system user on every start, so no user has to be created in advance. Together with sandboxing such as `ProtectSystem=strict` and `ProtectHome=yes`, the process can write only its own data directory: if you point the local channel somewhere else, open that directory with `ReadWritePaths=`; and the binary must not live under `/home` or `/root`, or the service cannot start; the script rejects such a `--dir`.
- **Restart=always.** Restoring a snapshot in the console makes the service exit cleanly; systemd starts it again 3 seconds later and the snapshot is swapped in before the database is opened. `Restart=on-failure` does not restart a clean exit, so the restore would only complete once someone starts the service by hand.
- **TimeoutStopSec=15s.** On stop, the service first stops accepting new requests and waits up to 6 seconds for in-flight ones (forcibly closing what remains), then cancels background loops and in-flight replication jobs and waits up to 2 seconds, then closes the database and exits. That is about 8 seconds in total; 15 leaves headroom. Cancelled replication jobs are re-queued by reconciliation on the next start.
- **UMask=0077.** New files, `master.key` included, are readable and writable only by the service user.
- **RCLONE_CONFIG=** (empty). ImgYoung hands channel parameters to rclone directly and never reads an rclone config file.

Everyday commands:

```bash title="Terminal"
systemctl status imgyoung        # service status
journalctl -u imgyoung -f        # follow the logs
sudo systemctl restart imgyoung  # apply changed environment variables
```

### Changing the service configuration [#changing-the-service-configuration]

Re-running the script with `--service` overwrites `/etc/systemd/system/imgyoung.service` with the version from the release archive, and says so when the content changes. Keep local changes out of that file and put them in a drop-in instead:

```bash title="Terminal"
sudo systemctl edit imgyoung
```

In the editor that opens, add the settings to override. For example, to listen on loopback only when a local reverse proxy is the sole entry point:

```ini title="/etc/systemd/system/imgyoung.service.d/override.conf"
[Service]
Environment=IMGYOUNG_LISTEN=127.0.0.1:8721
```

Save, then run `sudo systemctl restart imgyoung`. The available environment variables are listed in [Configuration](../configuration.mdx).

## Upgrading [#upgrading]

Run the same command you installed with:

- Installed with `--service`: the binary is replaced, the unit file is rewritten and the service is restarted onto the new version. You must pass `--service` again; otherwise only the file on disk changes and the running process stays on the old version.
- Installed without `--service`: only the binary is replaced; restart the process yourself.

Use `--version` to stay on a particular version. The database schema is migrated automatically when the new version starts.

`--version` can also install an older version, but that is not a downgrade: an older version refuses to start on a database migrated by a newer one, and the error names the latest migration in the database and in the binary. Rolling back takes the older version plus a snapshot taken before the upgrade; see [Upgrading](../operations/upgrade.mdx) for the steps and what to prepare beforehand.

## Uninstalling [#uninstalling]

With the service installed:

```bash title="Terminal"
sudo systemctl disable --now imgyoung
sudo rm /etc/systemd/system/imgyoung.service
sudo rm -rf /etc/systemd/system/imgyoung.service.d   # only exists if you used systemctl edit
sudo systemctl daemon-reload
sudo rm /usr/local/bin/imgyoung
```

The data directory is left in place. Delete it only once you are sure you no longer need the images, the database and `master.key` in it:

```bash title="Terminal"
sudo rm -rf /var/lib/private/imgyoung /var/lib/imgyoung
```

If only the binary was installed, delete it (`~/.local/bin/imgyoung` or `/usr/local/bin/imgyoung` by default), then delete whichever data directory you ran it with, if you no longer need it.

## Mirrors and internal distribution [#mirrors-and-internal-distribution]

If GitHub is hard to reach, host the release files on a mirror or an internal server and point `IMGYOUNG_RELEASES` at it. The variable has to reach the `sh` that runs the script, not `curl`:

```bash title="Terminal"
curl -fsSL https://mirror.example.com/imgyoung/install.sh \
  | sudo IMGYOUNG_RELEASES=https://mirror.example.com/imgyoung/releases sh -s -- --service
```

`install.sh` itself must be reachable there too. The mirror must use the same layout as GitHub Releases:

```text
<IMGYOUNG_RELEASES>/latest                                   redirects to .../tag/vX.Y.Z
<IMGYOUNG_RELEASES>/download/vX.Y.Z/imgyoung_X.Y.Z_linux_amd64.tar.gz
<IMGYOUNG_RELEASES>/download/vX.Y.Z/checksums.txt
```

`/latest` is only used without `--version`: the script follows the redirect and treats the last segment of the final URL as the version, which must start with `v` followed by a digit. If the mirror cannot redirect like that, always pass `--version` when installing and upgrading.
