당신은 systemd 타이머를 충분히 좋아하지 않는군요
(blog.tjll.net)- systemd 타이머는
cron의 실무적 대체물로, 일정에 따라.service같은 유닛을 실행하고 이력·출력·환경 관리를 더 명확하게 해줌 - 전통적
cron은 모호한$PATH, 유실되기 쉬운stdout/stderr, 추적하기 어려운 실행 이력, 읽기 힘든 스케줄 문법이 약점임 - 타이머는 같은 스템의
.timer와.service를 연결하고,OnCalendar,OnBootSec,OnUnitActiveSec로 시각·이벤트 기준 실행을 표현함 systemd-analyze calendar와systemctl list-timers로 시간 표현과 다음 실행 시각을 확인할 수 있고,WakeSystem=은 절전 상태에서도 실행을 깨울 수 있음RandomizedOffsetSec와FixedRandomDelay=는 동시 실행 피크를 줄이고,Persistent=는 비활성 중 놓친 실행을 온라인 직후 보완함
systemd 타이머를 cron 대체로 쓰는 이유
cron job은 실제cron데몬이 아니어도 “매일 이것을 실행”, “매달 저것을 실행”처럼 일정에 따라 작업을 실행하는 컴퓨팅 기본 요소를 가리키는 말로 널리 쓰임- systemd timer는 특정 일정에 따라 다른 유닛, 보통
.service를 실행하는 systemd 유닛이며, 전통적인cron데몬의 기능적 대체물이 될 수 있음 - 전통적인
cron에는 몇 가지 실무상 약점이 있음- 모호한
$PATH설정 때문에 스크립트 실행 결과를 예측하기 어려움 stdout과stderr출력이 블랙홀로 들어가거나 호스트의 메일 시스템으로 보내지는 일이 많음- 실행 이력을 추적하고 질의하기 어려움
01,31 04,05 1-15 1,6 *같은 스케줄 문법은 사람이 읽기 쉽거나 직관적이지 않음
- 모호한
- systemd 타이머는 이런 문제를 줄이면서
cron스타일 표현과 비슷한 캘린더 설정도 제공함
기본 구조: 서비스와 타이머
- systemd 타이머는 실행 대상이 필요하며,
.service유닛은 논리적으로 스크립트처럼 볼 수 있음 - 예시로
/etc/systemd/system/roulette.service에 다음 유닛을 두면 10분의 1 확률로 컴퓨터를 종료하는 서비스를 설치하게 됨
[Unit]
Description=1 in 10 chance to break your chains
[Service]
ExecStart=/usr/bin/env bash -c '[[ $(($RANDOM % 10)) == 0 ]] && systemctl poweroff || echo LIVE ANOTHER DAY'
ExecCondition=은 조건부 실행을 systemd 서비스 옵션으로 표현하는 더 통합된 방식이며, “계속 실행해야 하는가?”를 유닛 수준에서 더 명확하게 드러냄
[Unit]
Description=1 in 10 chance to break your chains
[Service]
ExecCondition=/run/current-system/sw/bin/bash -c '[[ $(($RANDOM % 10)) == 0 ]]'
ExecStart=/run/current-system/sw/bin/systemctl poweroff
- 조건이 충족되지 않으면 저널에 더 명확한 문구가 남음
May 05 11:05:32 diesel systemd[3117]: Condition check resulted in 1 in 10 chance to break your chains being skipped.
- 일반적으로 systemd가 제공하는 옵션을 활용하는 편이 직접 스크립팅하는 것보다 더 나은 경험을 줌
OnFailure=는 서비스 스크립트 실패에 반응할 때 쓸 수 있음Restart=는 일시적 실패에서 복구를 시도할 때 쓸 수 있음
타이머 유닛 연결과 실행
- 같은 파일 스템을 가진
/etc/systemd/system/roulette.timer를 두면roulette.service와 타이머를 연결할 수 있음
[Unit]
Description=impending destruction
[Timer]
OnCalendar=10:00
[Install]
WantedBy=timers.target
- 기본적으로 타이머의
Unit=설정은 같은 스템에.service가 붙은 서비스 유닛을 선택함- 이 예시에서는
roulette.service가 선택됨 - 다른 이름의 서비스 유닛을 실행하려면
Unit=을 바꿀 수 있음
- 이 예시에서는
ExecStart=대상은 기본적으로 셸 명령으로 실행되지 않음- 절대 경로 대상은 스크립트나 문자열 인자로 스크립트를 기대하는 인터프리터처럼 다뤄야 함
ExecStart=/usr/bin/echo Hello | /usr/bin/awk는 이 문맥에서 파이프가 의미 없기 때문에 동작하지 않음
ExecStart=인자는 기본적으로 일부 시스템 매니저 기본값 외의 환경 변수를 상속하지 않음- 기본
$PATH는 거의 비어 있는 상태에 가까움 /usr/bin/env를 실행하면systemctl같은 항목을 사용할 수 있게 하는 간단한 보호 장치가 됨ExecStart=/usr/bin/bash만 썼다면$PATH에 기본 항목이 들어가지만,env사용은 추가 안전장치임
- 기본
- 타이머 없이 서비스를 직접 실행할 수 있음
systemctl start roulette
[Install]섹션이 없는 서비스는enable할 수 없으며, 이 구조에서는 타이머가 서비스를 일관되게 실행하는 표준 방식임systemctl은 명시적 접미사가 없어도 기본적으로roulette.service에 대해 동작함.timer유닛에systemctl start를 적용하면 타이머를 작동 상태로 만들지만, 실제Unit=대상 서비스를 즉시 실행하지는 않음
systemctl start roulette.timer
status는 타이머가 다음에 언제 실행될지 보여줌
systemctl status roulette.timer
Trigger: Sat 2026-04-18 10:00:00 MDT; 35min left
- 가장 단순한 흐름은 실행 대상 서비스를 만들고, 일정이 있는 타이머를 같은 위치에 두고, 대상이 아니라 타이머를 시작하는 방식임
- 타이머 유닛의
[Install]에WantedBy=가 있으면 부팅 시에도 타이머가 올라오게 할 수 있음
systemctl enable roulette.timer
시간 표현: 캘린더 이벤트와 기간
- 타이머에서는 일정 표현 방식이 중요하며, 반복되는 시간 구간과 캘린더 이벤트 또는 타임스탬프를 구분해야 함
systemd.time(7)매뉴얼은 예제가 충분하고 타이머 작성 시 첫 번째 참고 자료로 쓸 만함systemd-analyze는 시간 표현식을 검증하고 설명할 수 있음
systemd-analyze calendar '*-*-* *:*:*'
Normalized form: *-*-* *:*:*
Next elapse: Sat 2026-04-18 16:44:26 MDT
(in UTC): Sat 2026-04-18 22:44:26 UTC
From now: 431ms left
- systemd 타이머는 반복되는 벽시계 기준 시각뿐 아니라, 전통적인
cron과 달리 어떤 이전 이벤트를 기준으로 반복되는 기간도 정의할 수 있음 daily의 완전한 형태는 매년, 매월, 매일 00:00:00에 실행된다는 뜻임
*-*-* 00:00:00
│ │ │ │ │ ╰── at second 00
│ │ │ │ ╰───── at minute 00
│ │ │ ╰──────── at hour 00
│ │ ╰────────── every day
│ ╰──────────── every month
╰────────────── every year
daily같은 축약어, 완전한 형식,systemd.time(7)의 다른 지원 값을 사용할 수 있고,systemd-analyze로 가정을 검증할 수 있음
이벤트 기준 실행이 더 적합한 경우
- 실제 작업에는 “매일 같은 시각에 실행”보다 “다른 이벤트 이후에 실행”이 더 잘 맞을 때가 많음
- 임시 디렉터리를 비우는 작업은 부팅 직후
cron표현식이 지나갔다면/tmp에 정리할 것이 거의 없을 수 있음 - “컴퓨터가 시작된 지 한 시간 뒤 실행하고 그 뒤 매시간 실행”이라고 표현하면 서비스의 실제 동작과 일정 논리가 더 잘 맞음
[Timer]
OnBootSec=1h
OnUnitActiveSec=1h
OnBootSec=1h는 머신 시작 1시간 뒤 한 번 실행한다는 뜻임OnUnitActiveSec=1h는Unit=이 실행된 지 1시간 뒤 다시 실행한다는 뜻이며, 타이머가 암묵적으로 계속 반복되게 만듦- 이런 주기적 기간 표현은 “매시간 이 분에 실행” 같은 표현보다 “가끔 한 번씩 실행” 용도에 더 자주 맞음
- Advent of Code API를 폴링하는 Slack 봇 예시에서는
*/15cron 표현식이 API의 “15분마다” 정책을 지키지만, 모두가 같은 식으로 폴링하면 트래픽이 몰릴 수 있음 - 코드 수정 뒤 타이머를 시작하고 15분이 지날 때마다 실행되게 하면 필요한 동작은 충족하면서 thundering herd 문제를 줄일 가능성이 있음
타이머 상태를 한눈에 보기
systemctl list-timers는 한 머신의 타이머 상황을 요약해서 보여주는 고수준 명령임
systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2026-04-20 15:15:00 MDT 1min 40s Mon 2026-04-20 15:00:05 MDT 13min ago zfs-snapshot-frequent.timer zfs-snapshot-frequent.service
Mon 2026-04-20 15:32:16 MDT 18min Mon 2026-04-20 14:22:15 MDT 51min ago fwupd-refresh.timer fwupd-refresh.service
Mon 2026-04-20 16:00:00 MDT 46min Mon 2026-04-20 15:00:05 MDT 13min ago logrotate.timer logrotate.service
Mon 2026-04-20 16:00:00 MDT 46min Mon 2026-04-20 15:00:05 MDT 13min ago zfs-snapshot-hourly.timer zfs-snapshot-hourly.service
Tue 2026-04-21 00:00:00 MDT 8h Mon 2026-04-20 09:43:22 MDT 5h 29min ago zfs-snapshot-daily.timer zfs-snapshot-daily.service
Tue 2026-04-21 07:31:28 MDT 16h Sun 2026-04-19 20:15:47 MDT 7h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2026-04-27 00:00:00 MDT 6 days Mon 2026-04-20 09:43:22 MDT 5h 29min ago zfs-snapshot-weekly.timer zfs-snapshot-weekly.service
Mon 2026-04-27 01:09:27 MDT 6 days Mon 2026-04-20 09:43:22 MDT 5h 29min ago fstrim.timer fstrim.service
Mon 2026-04-27 04:28:38 MDT 6 days Mon 2026-04-20 09:43:22 MDT 5h 29min ago zpool-trim.timer zpool-trim.service
Fri 2026-05-01 00:00:00 MDT 1 week 3 days Wed 2026-04-01 10:07:51 MDT 1 week 1 day ago zfs-snapshot-monthly.timer zfs-snapshot-monthly.service
Fri 2026-05-01 03:17:17 MDT 1 week 3 days Wed 2026-04-01 10:07:51 MDT 1 week 1 day ago zfs-scrub.timer zfs-scrub.service
11 timers listed.
Pass --all to see loaded but inactive timers, too.
- 한 명령만으로 타이머 일정에 따라 실행되는 항목의 전체 그림을 얻을 수 있음
list-timers는 자주 쓰이는 systemd 하위 명령 계열의 일부임list-units도 유용함list-paths는systemctl에 더 최근 추가된 하위 명령임
절전 상태에서 깨워 실행하기
WakeSystem=은 시간이 경과한 타이머가 시스템을 절전 상태에서 깨우도록 할 수 있음
WakeSystem=
Takes a boolean argument. If true, an elapsing timer will
cause the system to resume from suspend, should it be
suspended and if the system supports this.
...
- 이 기능은 사람이 노트북 덮개를 여는 물리적 동작을 하지 않아도 중요한 스크립트를 실행해야 할 때 유용함
- Arch나 NixOS처럼 사용 전 패키지 업데이트 다운로드를 지원하는 배포판에서는 밤늦게 업데이트 패키지를 미리 가져오고, 아침에 키보드 앞에서 업데이트할 수 있음
.service가 끝난 뒤 다시 절전 상태로 들어가게 하려면 수동으로 다시 절전 처리해야 한다고 매뉴얼에 나와 있음
실행 시각 분산과 thundering herd 완화
- thundering herd 문제는 여러 프로세스가 동시에 깨어날 때 생기는 시스템 문제임
- 전 세계 Debian 시스템이 모두
00:00:00에apt update하도록 하드코딩되어 있다면, 자정은 모두에게 나쁜 트래픽 피크 시간이 됨 FixedRandomDelay=와RandomizedOffsetSec=는 실행 시각을 분산하는 데 도움이 됨
FixedRandomDelay=
Takes a boolean argument. When enabled, the randomized delay
specified by RandomizedDelaySec= is chosen deterministically,
and remains stable between all firings of the same timer,
even if the manager is restarted. ...
RandomizedOffsetSec=
Offsets the timer by a stable, randomly-selected, and evenly
distributed amount of time between 0 and the specified time
value. ...
- 소프트웨어 업데이트를 확인하는 실제 시스템에서 이런 설정을 사용할 수 있음
- 실행을 균등 분포로 퍼뜨리면 thundering herd 문제를 줄이고, 동작을 일관되게 만들며, 분산 서비스를 조율 중인 데몬 재시작 같은 방해 활동을 피하는 데 도움이 됨
- 타이밍 옵션은 전반적으로 매우 설정 가능하고 세밀한 제어를 제공함
놓친 실행을 즉시 보완하기
Persistent=는 절전 중인 노트북 때문에 건너뛰면 안 되지만WakeSystem=까지는 필요하지 않은 일정 스크립트에 특히 적합함
Persistent=
Takes a boolean argument. If true, the time when the service
unit was last triggered is stored on disk. When the timer is
activated, the service unit is triggered immediately if it
would have been triggered at least once during the time when
the timer was inactive. ...
- 구성 관리 체크인을 예약한 시스템이 다운타임을 겪었다면,
.timer에Persistent=를 두는 것만으로 온라인 직후 올바른 상태로 수렴할 수 있음 Persistent=가 없으면 타이머의 정상 실행 시각까지 기다려야 할 수 있고, 그 시간이 길어질 수 있음- 놓친 활성화를 감지했을 때 기다리면 안 되는 다른 작업으로 시스템 업데이트, 배치 작업 확인 등이 있음
타이머 작성 시 주의할 점
systemctl --user로 다루는 사용자 매니저 문맥의 타이머도 유효하지만,[Install]에 쓰는 대상에 주의해야 함- 배포판에 따라 사용자 타이머의 적절한 대상은
default.target일 수 있음 cron과 마찬가지로 정확한 시스템 시계를 유지해야 한다는 일반적인 주의사항은 그대로 적용됨- systemd 사용자는
timedatectl timesync-status로 동기화 상태를 확인할 수 있음 - 많은 편집기는 systemd 유닛 파일 형식을 기본 지원하며, 유닛 파일이 커질 때 도움이 됨
- Emacs에서는 emacs systemd 패키지를 사용할 수 있음
댓글과 토론
Hacker News 의견들
-
systemd 타이머를 충분히 써본 건 아니라 반박하긴 어렵지만, cron의 $PATH가 모호하다는 말은 잘 모르겠음
crontab 안에서 바로PATH를 설정할 수 있는데,/etc/bashrc,~/.bashrc,~/.profile,~/.bash_profile,/etc/systemd/…같은 곳에서 설정되는 것보다 예측하기 어렵다고 볼 이유가 있나 싶음
1994년부터 Linux를 썼지만 cron 문법을 외우진 않음. 다만 crontab 주석에m h dom mon dow command가 미리 적혀 있어서 제목에 맞춰 숫자를 넣으면 됨
나머지 불만은 이해되고, 다음에 cron 작업이 필요하면 systemd 타이머를 한번 써볼 생각임- “제목에 맞춰 숫자를 넣으면 된다”는 건 원래 지적을 공정하게 요약한 게 아님. 그게 전체 문법이 아니기 때문임
쉼표, 슬래시, 별표, 조합이 있고, 무작위화가 필요하면 cron 자체로는 일반적으로 못 해서 명령 안에 넣어야 함. 사소하지 않은 cron 명세를 쓰는 건 쉽지 않음 - cron 표현식을 지원한다는 애플리케이션을 다뤄본 입장에선, 숫자는 언어의 가장 기본적인 부분일 뿐임
누군가5,3/4 4-8,11 1 4,5,6,9-11 */2같은 걸 넣으면 실제로 뭘 의도했는지 역공학해야 하는 재미가 생기는데, 보통 작성한 그대로의 의미가 아님
여기에 일부 cron 환경에서만 지원되는 확장까지 들어가면 더 복잡해짐
systemd 타이머는 훨씬 관리하기 좋았음. 오래 도는 작업의 중복 실행 허용 여부를 제어하거나, 고정 시각이 아니라 시작-종료 사이에 작업을 실행하는 기능은 큰 개선임
예전에 백업 작업이 심볼릭 링크 루프에 빠져 VPS가 내려간 적이 있는데, cron은 끝나지 않은 백업이 있는데도 계속 새 백업 작업을 띄웠음
CRON만의 특수한PATH때문에 명령과 스크립트를 다시 써야 했던 것도 불편했지만, 일부 systemd 타이머에서도 비슷할 수는 있음. 그래도 crontab을 30초 뒤 실행되게 바꾸고 기다릴 필요 없이 타이머를 수동 실행할 수 있음 - crontab에
$PATH를 하드코딩해서 cron 작업을 테스트하고 싶진 않음
하드코딩하지 않으면 cron이 실행할 때의$PATH와 직접 명령을 시험할 때의$PATH가 달라짐.systemctl start foo.service는 타이머가 발화할 때와 같은 환경에서 명령을 시작하므로 같은 방식으로 동작할지 알 수 있음
반대로 cron 작업은 crontab에 지정한 시각에 실행됨. systemd 타이머는 대부분 지정 시각에 발화하지만, systemd의 논리 버그 때문에 2월 29일에 한 번 실행된 뒤 다시는 실행되지 않거나, 타이머 유닛을 “restart”했을 때 발화할 수도 있고 안 할 수도 있음 - systemd 환경의 장점은 표준적이고 거의 빈 상태라는 점임. 반면 crontab 환경은 supervisord나 sysvinit 스크립트가 물려받는 환경과 완전히 달라서 자주 당했음
systemd에서는 무엇이 트리거하든 실제 실행되는 유닛이 같아서 간극이 없음
기본 환경이 뭔지는 여전히 알아야 하지만, 셸의 영향을 받지 않는 거의 깨끗한 환경임. 이건 systemd의 장점이라고 봄 PATH가 모호하다고까지는 말하지 않겠지만, cron에는PATH관련 문제가 있음
기본값에/usr/local/bin이나 root용/usr/sbin처럼 기대할 만한 값이 빠져 있고, Arch Linux 같은 일부 배포판은 매뉴얼 페이지에 기본 경로나 설정 권장조차 안 적혀 있음
특정 스크립트 하나에만 경로를 추가하려면env로 감싸거나 래퍼 스크립트에서 설정하거나, 항목 앞에서 경로를 바꾸고 뒤에서 되돌려야 함
PATH안에서~나$HOME을 못 쓰고 전체 절대 경로를 써야 해서 사용자 crontab에서는 특히 귀찮음
우회가 어렵진 않지만, 기본적으로 다른 서비스와 같은 경로를 쓰는 systemd 타이머가 더 나은 경험이라고 봄
- “제목에 맞춰 숫자를 넣으면 된다”는 건 원래 지적을 공정하게 요약한 게 아님. 그게 전체 문법이 아니기 때문임
-
cronie에서 systemd 타이머로 옮긴 이유는 시스템 시작 시각에 더 탄력적이기 때문임
백업 전략은 매일 고정 시각에 borg 아카이브 항목을 만드는 것인데, cronie는 예약 시각에 시스템이 켜져 있어야 함. systemd 타이머는 이 상황을 견디고 시스템이 사용 가능해지는 즉시 서비스를 실행함
백업 자동화 저장소는 https://github.com/gchamon/borg-automated-backups임- Cronie에는 이를 위한 anacron이라는 메커니즘이 있음. 내 시스템에서는 cron이 매시간
/etc/cron.hourly/0anacron을 호출하고, 가장 이른 실행 시각을 놓쳤더라도/etc/cron.{daily,weekly,monthly}작업을 수행함. 무작위 지연도 설정 가능함
/etc/anacrontab을 수정하면 사용자 지정 스케줄을 만들 수 있음
사용자 수준에서는@hourly anacron -t /path/to/anacrontab -S /path/to/spooldir같은 항목을 사용자 crontab에 넣을 수 있을 텐데, 직접 해보진 않았음
많은 cron 구현에도 비슷한 메커니즘이 있음 - cron에는
@reboot옵션이 있고, 몇몇 스크립트에 쓰고 있는데 잘 동작함 - Cronie에는
@reboot메타 트리거가 없나?
- Cronie에는 이를 위한 anacron이라는 메커니즘이 있음. 내 시스템에서는 cron이 매시간
-
Canon 프린터 노즐이 한동안 놀면 막힐 수 있어서 못 믿겠음. 그래서 Claude에게 systemd 스크립트를 만들게 해서 매주 강아지 사진을 출력하게 했고, 프린터에 부담을 주도록 CMYK 스펙트럼이 충분히 들어가게 함
월요일마다 책상에 앉으면 프린터에서 갑자기 사진이 튀어나오는 게 꽤 기분 좋은 놀라움임- 프린터가 며칠마다 잉크를 스펀지에 낭비하며 빼내는 대신, 앨범이나 달력에서 무작위 이미지를 출력하는 모드가 있으면 좋겠음
적어도 아이들 과학 경진대회 프로젝트 아이디어 정도는 될 수 있겠음 - 아버지가 Deskjet 720 비슷한 모델을 갖고 있었음
돌아가신 뒤 몇 년 동안 전원도 꺼진 채 안 쓰였는데, 컬러 출력이 필요해 전원만 연결하고 출력했음
페이지 1/5 정도 지나자 모든 색이 돌아왔고, 그 뒤로 20페이지 정도를 문제없이 출력했음 - 대학을 처음 다니던 시절 오래된 Samsung ML-2010으로 비슷한 걸 했음
하드웨어보다는 소프트웨어 문제 같았는데, 프린터를 컴퓨터에 연결해두고 일주일 넘게 놀리면 그냥 출력이 멈췄음
로그를 파고들 수도 있었겠지만, 대신 월요일과 목요일마다 테스트 페이지를 출력하는 cron 작업을 만들었음. 테스트 페이지 상단에는LOL PRINTER WORKS같은 문구만 있었음
생각보다 낭비는 아니었음. 수학 과목을 많이 듣고 있어서 문제 풀이용 이면지가 엄청 필요했고, 실패 출력물이나 테스트 출력물을 먼저 쓰고 나서 빈 종이를 썼음 - 레이저 프린터가 답임. 소모품 절약만으로도 본전을 뽑을 수 있음
- 저렴한 OKI LED 컬러 프린터를 추천하려 했는데, 소비자 시장에서 철수해버렸음
최대 해상도는 600dpi뿐이어도 색이 아주 좋고 균일했으며, 토너가 마르지 않는 게 형의 핵심 구매 기준이었음. HP 잉크젯은 여러 번 막혔음
- 프린터가 며칠마다 잉크를 스펀지에 낭비하며 빼내는 대신, 앨범이나 달력에서 무작위 이미지를 출력하는 모드가 있으면 좋겠음
-
systemd 타이머를 아주 좋아함. Ansible로 배포하던 cron 작업을 천천히 전부 타이머로 옮겼고, 이제는 Ansible 복사만 하면 됨
journalctl과의 통합이 특히 좋고, Debian 13처럼 syslog가 사라진 최신 OS에서는 더 좋음. 디버깅을 위해 서비스를 수동 시작할 수 있는 것도 편함
동작하지 않는 cron 작업은 복붙하거나 추가 셸 스크립트를 써야 하는 귀찮은 일이었고, cron 작업의 표준 출력이 사라지는 블랙홀은 말할 것도 없음
기존처럼 systemd 서비스를 모니터링하고 실패 알림을 받을 수 있음. 오픈소스 프로젝트들이 배포 방식으로 타이머를 점점 더 권장하는 것도 좋게 봄- 프로젝트가 타이머를 권장하는 건 괜찮지만, 무시하고 cron을 쓸 수 있어야 만족함
-
NixOS는 systemd가 기본이라 관리의 일급 요소로 쓰고 있음. macOS의 launchd를 쓰다 오면 특히 좋음
NixOS용 도구를 배포할 때 systemd를 덧붙인 땜질처럼 쓰지 않고 자연스럽게 활용할 수 있다는 점도 좋음
다만 Linux 사용자 전체를 대상으로 수명주기 관리가 많은 도구를 배포한다면, systemd가 어디에나 있는 건 아니라서 어떻게 해야 할지 궁금함
btrfs 풀의 월간 scrub을 systemd 타이머로 돌리고 있음. 사용자가 직접 scrub을 시작하면 다음 예약을 건너뛰거나, 월간 작업인데 기기가 6개월 꺼져 있었을 때 작업을 누적할지 단일 작업으로 접을지 등을 정할 수 있어서 꽤 유용함- NixOS에서는 systemd 작업이 정말 쉬움. INI 파일을 붙잡고 씨름하는 것보다 Nix로 유닛 정의하는 편이 낫음
systemd.services.sync-recyclarr = { serviceConfig.Type = "oneshot"; path = [ pkgs.podman ]; script = '' podman exec -it recyclarr recyclarr sync radarr podman exec -it recyclarr recyclarr sync sonarr ''; };
systemd.timers.sync-recyclarr = { timerConfig = { OnCalendar = "daily"; Persistent = true; Unit = "sync-recyclarr.service"; }; partOf = [ "sync-recyclarr.service" ]; requires = [ "podman-recyclarr.service" ]; wantedBy = [ "timers.target" ]; }; flake.nix파일 안에 직접 정의하고 있는지 궁금함
나도 NixOS를 쓰지만 설정은 모두 원래 형식으로 유지하고 Nix로 심볼릭 링크만 걸어둠. 그래야 non-NixOS 시스템에서도 설정을 쉽게 가져다 쓸 수 있음
문제는 NixOS가~/.config/systems/user폴더에 둔 systemd 타이머와 서비스를 잡아 실행하지 않는 듯하고,WantedBy=default.target같은 것도 효과가 없다는 점임
그래서 재부팅 후 모든 서비스를 수동 재시작하고 나면 systemd 타이머가 멋지다는 데 동의하게 됨
- NixOS에서는 systemd 작업이 정말 쉬움. INI 파일을 붙잡고 씨름하는 것보다 Nix로 유닛 정의하는 편이 낫음
-
타이머는 꼭 같은 이름의 서비스 유닛만이 아니라 임의의 유닛과도 함께 동작해서 의외로 유연함
내 서버에는 매일 아침 무작위 시작 시각과 알림을 포함해restic backup,restic prune,restic forget전체 백업 사이클을 실행하는backup.target을 시작하는 타이머가 있음
실제restic-*유닛은 Podman Quadlet이라, 서버에 Podman과 Systemd만 있으면 구성 자체는 서버 내용과 무관하게 돌아감
다만 타이머는 일상적으로 쓰기엔 가장 투박한 systemd 유닛 유형 중 하나임. 두 파일로 나뉘고 start와 enable 문법이 다른 이유는 이해하지만, 가끔은 그냥 파일 하나 만들어 스크립트를 실행하고 끝내고 싶음- 백업에서 비슷한 구성을 씀. 가끔 restic 저장소에서 잠금 정리 같은 다른 작업을 하려고 타이머를 비활성화했는데, 이미 트리거된 유닛이 여전히 돌고 있다는 걸 못 보고 당한 적이 있음
- systemd 유닛 위에 추상화 계층이 하나 더 필요해 보임. 파일을 직접 편집하는 대신 어떤 선언형 명령줄 도구가 처리해주는 식이면 좋겠음
LLM 시대에는 별 문제가 아닐 수도 있지만, 매번 약간씩 번거롭게 느껴짐 - 백업 시간을 왜 무작위화하는지 궁금함
-
systemd의 어떤 면을 항상 크게 좋아한 건 아니지만, 이 감상에는 대체로 동의함
“시스템” 수준의 예약 작업에서는 cron 사용을 거의 완전히 그만두고 systemd 타이머를 선호하게 됨. 특정 애플리케이션 범위의 스케줄링에는 Quartz 같은 걸 넣을 수도 있음
이유는 흐릿하고 설명하기 어려운데, systemd 방식이 “작동 방식은 이래야 한다”는 내 머릿속 모델에 더 깔끔하게 맞음. 과거에 cron 스크립트 실행에서PATH가 모호해 예측하기 어려웠던 일도 꽤 겪었고, 그게 전부는 아님
systemd 타이머가 보편적·객관적으로 cron보다 낫다고 주장하진 않겠지만, 적어도 나를 설득하는 데는 성공했음 -
systemd 타이머 입문으로 아주 좋았고, 드디어 써보게 만들 정도로 설득됐음
list-timers도 마음에 듦. cron에서는 한 머신에서 돌아가는 모든 cron 작업을 한눈에 파악하기가 쉽지 않았음. 모든 사용자의 crontab,/etc/cron.d/, daily/hourly/monthly 디렉터리를 확인해야 했음
실제로 부팅 후 약 5분 뒤 한 번 실행하고 이후 약 12시간마다 실행해야 하는 용례가 있는데, systemd 타이머가 이걸 처리해준다니 좋음- 좋은 말 고마움. systemd가 지금처럼 널리 쓰이는 상황에서는
systemd-analyze와systemctl list-timers같은 도구 사용법을 익히는 게 정말 가치 있다고 느낌
- 좋은 말 고마움. systemd가 지금처럼 널리 쓰이는 상황에서는
-
systemd는 처음 보면 복잡하지만, 쓰다 보면 다른 걸 쓰고 싶지 않게 됨. 모든 걸
systemctl로 관리하는 게 편함- 설계에서 아직 완전히 이해되지 않는 이상한 선택들이 있음
예를 들어 유닛 정의가 왜 반드시 디스크의 실제 파일이어야 하는지 모르겠음. 데몬을 다시 읽을 때 변경된 파일만이 아니라 모든 파일을 다시 읽음. 프로그래밍 방식으로 유닛을 추가하는 API가 있으면 안 됐을까 싶음. 비슷한 건 있지만 제약이 많고 유연하지 않음
한 파일 안에 여러 유닛을 선언할 수 없는 것도 의문임. 파일시스템 중심으로 설계되어 있고, 다른 수준의 추상화를 하지 않은 선택은 똑똑해 보이지 않음. 딱히 Unix 철학을 따르는 것도 아님
유닛 정의 형식도 TOML이 그때 있었더라면 더 합리적인 걸 썼을 텐데 싶음 - systemd에는 실제로 유용한 매뉴얼 페이지가 있음
- 다르게 보는 사람도 있음. 써보고 나서 쓰고 싶지 않아지는 쪽임. systemd를 진심으로 싫어함
물론 내가 늙어서 그런 거겠지. 당연히systemd-*만이 유일하게 옳은 길이고, 다르게 보는 사람은 전부 훈수꾼일 테니까 - 여전히 journald 로깅은 싫음
- 꽤 현대적인 관점임. systemd가 처음 나왔을 때는 사람들이 정말 싫어했지만, 나는 늘 좋아했고 결국 사람들이 받아들일 거라고 봤음. 이제 정말 그렇게 된 걸 보니 좋음
- 설계에서 아직 완전히 이해되지 않는 이상한 선택들이 있음
-
Linux를 20년 넘게, systemd를 10년 넘게 써왔음
그런데도 늘 새로 배울 게 있고, 실제로 또 하나의 유용한 도구로 고려하게 됨- Linux를 30년쯤 쓰고 있는데, apparently 우리는 수십 년 동안 cron을 쓰며 전부 틀렸던 셈임
Lobste.rs 의견들
-
systemd가 완벽하진 않지만, 많은 설계 결정은 더 전통적인 과거 방식에서 얻은 배움을 바탕으로 한다는 느낌이 듦
최근 Lennart Poettering이 그 배경을 설명한 CRE의 2015년 에피소드를 다시 들었는데, 지금도 추천할 만함 -
뼛속까지 systemd를 안 좋아하는 쪽이지만, systemd.timers는 이 제품에서 “그나마 나은” 개념 중 하나라고 봄
그래서 글쓴이가 타당한 불만을 가진 사람들을 깎아내리는 식으로 방어한 건 좀 의외였음
그래도at명령과 함께 쓰는 건 좋음. 특정 시각에 한 번 실행할 명령은at, 그 외에는 systemd 타이머와 단순한 유닛 파일을 쓰는 식임
가장 보고 싶은 개선점은 어느 사용자가 타이머를 실행 중인지 알 수 있게 하는 것임. 2026년에 셸박스를 운영하는 몇 안 되는 사람 중 하나긴 하지만, 매초 디스크를 두드리는 타이머를 어떤 사용자가 만들었는지 알 수 있으면 유용함- 이 목적이라면 모두가 시스템 타이머를 설치하게 하는 대신 사용자 유닛을 쓸 수 있지 않을까 싶음
loginctl enable-linger를 쓰면 활성 사용자 세션 없이도 실행될 수 있는 것으로 알고 있음. 물론 그걸로 충분하지 않은 용례도 있을 테고, 구체적인 상황은 모름
- 이 목적이라면 모두가 시스템 타이머를 설치하게 하는 대신 사용자 유닛을 쓸 수 있지 않을까 싶음
-
systemd 타이머는 특히 사용자 관리 쪽에서 초기 부담이 더 낮았으면 좋겠음
필요한 설정량을 보면crontab -e를 이기기가 정말 어렵다- 타이머 하나 설정하는 데 여러 설정 파일과 서비스를 요구하는 systemd 방식은… API 선택으로는 말이 안 될 정도임
-
cron 스크립트 로그를 체계적으로 모으는 방법을 오래 고민하다가, 그냥 systemd 타이머를 쓰면 된다는 걸 깨달음
로깅 문제가 해결됨. 이제 cron을 다시 쓸 이유가 없고, 더 일찍 알았으면 좋았겠음logger로 파이프하거나 로그 파일에>>로 붙이거나, 기본값 그대로 두고 이메일을 받으면 되지 않나?
-
구식이라고 해도 좋지만, 서버에서 나에게 닿는 이메일은 아직도 설정해 둠
자동화해 두면 새 호스트마다 공짜로 따라오고, 평소에도 꽤 편리함
예를 들어 멀티플렉서 하나 열고long_running_process | mail root@localhost -s "done $?"를 실행한 뒤 잊어버리는 식임 -
좋은 글이고, 나도 비슷한 글의 초안을 갖고 있다가 최근에 다시 참고해야 했음
나처럼 systemd 토끼굴로 들어간다면, 관련 프로젝트 폴더에 있는 유닛 파일과 타이머를/etc/systemd/system/로 심볼릭 링크해 두는 걸 추천함
systemd에 대한 한 가지 불만은 배포판이 설치한 유닛과 직접 작성한 유닛을 구분해 주지 않는다는 점인데, 심볼릭 링크로 그 분리를 직접 유지할 수 있음- 사실 그 구분은 경로가 담당함
시스템/패키지/배포판 유닛은/usr/lib/systemd/system에 들어가고, 로컬 오버라이드나 로컬 유닛은/etc/systemd/system에 들어감
- 사실 그 구분은 경로가 담당함