Project

General

Profile

Snipe-IT » History » Version 4

Redmine Admin, 10/31/2026 07:01 PM
deploy-publish.sh

1 4 Redmine Admin
*Chapter 4 of the dmz-web documentation · headings are numbered like the cheat sheets · index: [[Wiki]]*
2
3
{{>toc}}
4
5 2 Redmine Admin
Deployed by `~/deploy/snipeit-install.sh [domain] [--force] [--skip-npm-api]` (same
6
pattern as `add-wordpress-site.sh`; restarts NPM at step 6).
7
Containers `site-snipeit` (`docker.io/snipe/snipe-it`, Apache :80) +
8
`db-snipeit` (`mariadb:11`) on `net-snipeit`. Data volume `app-snipeit-data`
9
at **`/var/lib/snipeit`** (uploads, backups, OAuth keys) — app code is in the
10
image. Secrets: `snipeit-db-root-password`, `snipeit-db-password`,
11 1 Redmine Admin
`snipeit-app-key`.
12
13 2 Redmine Admin
Key env (behind NPM): `APP_URL=https://assets.mdsdefense.com`,
14
`APP_FORCE_TLS=true`, `APP_TRUSTED_PROXIES=*`, `SECURE_COOKIES=true`,
15
`DB_HOST=db-snipeit`, `APP_KEY` from secret. **`APP_KEY` must never change**
16 1 Redmine Admin
— it encrypts fields in the DB; back the secret up with the DB.
17
18 2 Redmine Admin
```bash
19 1 Redmine Admin
# generate APP_KEY once (before first start)
20
podman run --rm docker.io/snipe/snipe-it:latest php artisan key:generate --show
21
printf '%s' 'base64:...' | podman secret create snipeit-app-key -
22
23
# first start runs migrations; watch it
24
podman logs -f site-snipeit
25
26
# artisan inside the container
27
podman exec -it site-snipeit php artisan about
28
podman exec -it site-snipeit php artisan snipeit:backup
29
podman exec -it site-snipeit php artisan config:clear   # after env changes + restart
30
31
# DB (same MariaDB recipe as WordPress)
32
podman exec -it db-snipeit sh -c 'mariadb -uroot -p"$MYSQL_ROOT_PASSWORD"'
33
podman exec db-snipeit sh -c 'mariadb-dump -uroot -p"$MYSQL_ROOT_PASSWORD" snipeit' > ~/tmp/snipeit-$(date +%F).sql
34
```
35
36 4 Redmine Admin
## 4.1 First-run pre-flight (Configuration Check)
37 1 Redmine Admin
38 2 Redmine Admin
Screenshot (2026-10-25, Snipe-IT v8.8.3):
39 1 Redmine Admin
40 2 Redmine Admin
![Snipe-IT pre-flight behind NPM](snipeit-preflight.png)
41 1 Redmine Admin
42 2 Redmine Admin
Behind NPM the check shows **two red rows that are expected and can be ignored**;
43
"Next: Create database tables" stays enabled:
44
45
| Row | What it says | Why it is red here | Verdict | How it was verified |
46
|-----|--------------|--------------------|---------|---------------------|
47
| **URL** | "thinks your URL is https://… but your real URL is http://…" | NPM owns 443 and terminates TLS; it talks plain HTTP to `site-snipeit:80`, so the app sees `http`. `APP_URL=https://assets.mdsdefense.com` in the unit is correct. There is no `.env` to edit — config is the unit's `Environment=` lines. | Normal behind a TLS-terminating proxy — **ignore** | After setup: login works without a redirect loop and every link/redirect is `https://`. Header test below. |
48
| **Config File** | "cannot determine if your config file is exposed" | The check can't probe from outside. In the image `.env` is outside Apache's document root (`public/`). | **ignore** | `/.env` returns **403** through NPM (command below). |
49
| Email | test button | `MAIL_MAILER=log` | skip until the SMTP relay is on — but the From address must be valid even now (see Mail) | — |
50
51
Verification commands (websvcmaster):
52
53
```bash
54
# Config File: .env must not be served -> 403/404
55
curl -sI --resolve assets.mdsdefense.com:444:127.0.0.1 https://assets.mdsdefense.com:444/.env | head -1
56
57
# URL: the app must build https redirects when NPM's forwarded header is present
58
podman exec site-snipeit sh -c 'curl -sI -H "X-Forwarded-Proto: https" -H "Host: assets.mdsdefense.com" http://127.0.0.1/ | grep -i ^location'
59
#   https://assets.mdsdefense.com/...  -> OK
60
#   http://...                          -> APP_TRUSTED_PROXIES not honoured: set it to the
61
#                                          net-snipeit subnet (podman network inspect net-snipeit | jq -r '.[0].subnets[0].subnet'),
62
#                                          cp unit, daemon-reload, restart
63
# If OK here but the browser still gets http links: NPM isn't sending the header ->
64
#   proxy host -> Advanced -> proxy_set_header X-Forwarded-Proto https;
65
66
# Overall: 302 to /login (or /setup before setup) is the healthy answer
67
check-sites.sh | grep snipeit
68 1 Redmine Admin
```
69 2 Redmine Admin
70 4 Redmine Admin
## 4.2 Mail (SMTP relay)
71 2 Redmine Admin
72
Same Exchange Online IP connector as Redmine — no credentials. **From and Reply-To
73
must always be set to real addresses, even while the mailer is `log`.** The image's
74
default is the unresolved placeholder `${MAIL_ENV_FROM_ADDR}`, which is not a valid
75
address, so Snipe-IT answers HTTP 500 whenever it builds *any* email (creating a user,
76
the Test Email button, a password reset) — whatever the mailer. Found 2026-10-25:
77
500 while creating the first admin; the user was saved, only the email step failed.
78
The setup form pre-fills the same placeholder in its address field and rejects it (no `@`);
79
typing a real address there fixes the form only — the From used when a mail is sent still
80
comes from the environment variable, so the 500 stays until the unit is fixed.
81
82
`snipeit-install.sh` v1.1 writes the unit like this (v1.0 had the From lines commented out):
83
84
```ini
85
Environment=MAIL_FROM_ADDR=sysadmin@mdsdefense.com
86
Environment=MAIL_FROM_NAME=Snipe-IT
87
Environment=MAIL_REPLYTO_ADDR=sysadmin@mdsdefense.com
88
Environment=MAIL_REPLYTO_NAME=Snipe-IT
89
Environment=MAIL_MAILER=log
90
#Environment=MAIL_MAILER=smtp
91
#Environment=MAIL_HOST=mdsdefense-com.mail.protection.outlook.com
92
#Environment=MAIL_PORT=25
93
#Environment=MAIL_ENCRYPTION=tls
94
```
95
96
To fix a unit generated by v1.0 **and** switch SMTP on in one go (repo copy, websvcmaster;
97
for a v1.1 unit drop the last two `-e` lines, they would duplicate Reply-To):
98
99
```bash
100
cd ~/deploy
101
sed -i -e '/^Environment=MAIL_MAILER=log$/d' \
102
       -e 's/^#\(Environment=MAIL_\)/\1/' \
103
       -e 's/<tenant>\.mail\.protection\.outlook\.com/mdsdefense-com.mail.protection.outlook.com/' \
104
       -e '/^Environment=MAIL_FROM_NAME=/a Environment=MAIL_REPLYTO_ADDR=sysadmin@mdsdefense.com' \
105
       -e '/^Environment=MAIL_FROM_NAME=/a Environment=MAIL_REPLYTO_NAME=Snipe-IT' \
106
       quadlet/site-snipeit.container
107
grep -n MAIL quadlet/site-snipeit.container      # eight active MAIL_ lines (MAILER, HOST, PORT, ENCRYPTION, FROM x2, REPLYTO x2), no =log
108
```
109
110
Commit (root: `deploy-commit.sh -m "snipeit: From/Reply-To + SMTP via Exchange Online IP connector"`),
111
then go live — restarts only `site-snipeit` (a few seconds, other sites untouched):
112
113
```bash
114
cp ~/deploy/quadlet/site-snipeit.container ~/.config/containers/systemd/
115
systemctl --user daemon-reload && systemctl --user restart site-snipeit.service
116
podman exec site-snipeit printenv | grep ^MAIL_
117
```
118
119
**Testing mail.** GUI: Admin Settings → **General Settings** → *Test Email* (not under
120
Notifications). Command line alternative, with the actual SMTP error printed (websvcmaster;
121
it only sends a single message, nothing is changed). Use an **`@mdsdefense.com` recipient**:
122
123
```bash
124
podman exec site-snipeit php artisan tinker --execute='Mail::raw("Snipe-IT SMTP test", fn($m) => $m->to("sysadmin@mdsdefense.com")->subject("Snipe-IT test")); echo "sent\n";'
125
```
126
127
`sent` plus the mail in the mailbox = relay works. Another end-to-end check: log out →
128
*Forgot password*; that form asks for the **username**, not the e-mail address, and shows
129
"sent" even for an unknown username, so a wrong entry silently sends nothing. If the TLS handshake fails, set
130
`MAIL_ENCRYPTION=` (empty) and restart.
131
132
**Result 2026-10-26:** mail to an `@mdsdefense.com` mailbox is delivered (Redmine, which uses the
133
same connector, delivers too). The same test to an external address (gmail.com) is refused by
134
Exchange Online: `451 4.4.4 Mail received as unauthenticated, incoming to a recipient domain
135
configured in a hosted tenant which has no mail-enabled subscriptions. ATTR5`. TLS and
136
From/Reply-To are fine and the connector does recognise the host's IP (internal mail passes);
137
the refusal is on Exchange's side for non-tenant recipients. Cause not yet established — check
138
the connector in the Exchange admin center (type "Your organization's email server", status
139 1 Redmine Admin
On, and whether relay to external recipients is permitted) and use *Mail flow → Message trace*
140 2 Redmine Admin
for the failing attempt. Until then Snipe-IT can notify `@mdsdefense.com` addresses only.
141
142 4 Redmine Admin
## 4.3 Image versions and upgrades
143 2 Redmine Admin
144
**Pinned state (2026-10-26, commit `4a9a464`):** `site-snipeit` runs
145
`docker.io/snipe/snipe-it:v8.8.3` (build 26143-g80b2dc5768, same build that `latest` was at install);
146
`db-snipeit` runs MariaDB 11.8.9 pinned by digest (`mariadb@sha256:2439dcd7…`, the image that was
147
running — the `11.8` tag had already moved to a different image). `snipeit-install.sh` v1.1 still
148
defaults to `:latest`; for a new install pass `--app-image docker.io/snipe/snipe-it:v8.8.3`.
149
150
Why pinned: `latest` does not update by itself (podman does not re-pull an image that exists
151
locally), but it silently moves whenever the image is pulled or the container is recreated, and
152
Snipe-IT runs database migrations at start — an unplanned version jump changes the schema. With a
153
pin, the version in use is in the unit file and in git, and an upgrade is a deliberate edit.
154
155
**There is no upgrade from the GUI.** Snipe-IT shows a "new version available" notice (it checks
156
GitHub), but the code lives in the image; the only way to upgrade is to replace the image.
157
Anything changed inside the container is lost when the image changes.
158
159
Upgrade procedure (websvcmaster unless stated):
160
161
1. New release noticed (GUI notice, or the `snipe/snipe-it` tags on Docker Hub): note the tag, e.g. `v8.8.4`. Tags are `v<major>.<minor>.<patch>`; `-alpine` variants exist, the unit uses the non-alpine one.
162
2. **Backup first:** DB dump, the `app-snipeit-data` volume, the `snipeit-app-key` secret (the migration at step 5 cannot be undone without the dump).
163
3. Change one line in the repo copy: `Image=docker.io/snipe/snipe-it:v8.8.4`.
164
4. Commit (root: `deploy-commit.sh -m "snipeit: upgrade to v8.8.4"`), then `cp` the unit to `~/.config/containers/systemd/`, `systemctl --user daemon-reload`, `systemctl --user restart site-snipeit.service`. Only Snipe-IT is down for a few seconds.
165
5. Migrations run on start: `podman logs -f site-snipeit`, then `~/deploy/check-sites.sh` (expect 302 for assets), then log in and check Admin → System information for the version.
166
6. If it breaks: put the old `Image=` line back, restart; if the schema already changed, restore the DB dump.
167
168
MariaDB patch updates are the same idea: change the digest (or the `11.8` tag) on purpose, with a dump
169
taken first. Rules for unit files: a comment must be on its own line — `Image=… # note` makes the
170
comment part of the image name. Read the running digest with
171
`podman image inspect <image-id> --format '{{.Digest}}'`.
172
173
---
174 3 Redmine Admin
_Source: `dmz-web-podman-cheatsheet.md` v1.5 in the `dmz-web-deploy` repo ([[Releases]]). Edit the wiki and the repo copy together._