[{"data":1,"prerenderedAt":567},["ShallowReactive",2],{"blog-ru-zapusk-python-po-raspisaniyu-linux":3,"blog-counterpart-ru-zapusk-python-po-raspisaniyu-linux":558},{"id":4,"title":5,"body":6,"cluster":545,"ctaType":546,"date":547,"description":548,"extension":549,"intent":550,"meta":551,"navigation":163,"ogImage":552,"path":553,"primaryKeyword":554,"readTime":555,"relatedCases":556,"relatedCourse":545,"relatedService":126,"relatedServices":558,"seo":559,"slug":560,"stem":561,"tags":562,"updated":558,"__hash__":566},"blogRu\u002Fru\u002Fblog\u002Fzapusk-python-po-raspisaniyu-linux.md","Как запускать Python-скрипт по расписанию в Linux через systemd",{"type":7,"value":8,"toc":538},"minimark",[9,18,21,26,37,47,50,114,121,132,136,142,236,258,265,269,274,331,345,348,369,372,413,435,439,442,485,496,509,512,516,534],[10,11,12,13,17],"p",{},"Чтобы запускать Python-скрипт по расписанию на Linux-сервере, оформите его как ",[14,15,16],"code",{},"systemd"," service и привяжите к нему timer. Сервис задаёт пользователя, рабочую папку и команду запуска; timer отвечает за календарное расписание. Такой вариант подходит для отчётов и небольших фоновых задач, если сервер работает на systemd.",[10,19,20],{},"Ниже настроим запуск ежедневно в 08:30 по часовому поясу сервера. Python-код остаётся обычной программой: сначала он должен запускаться вручную из виртуального окружения.",[22,23,25],"h2",{"id":24},"подготовьте-проект","Подготовьте проект",[10,27,28,29,32,33,36],{},"Допустим, проект расположен в ",[14,30,31],{},"\u002Fopt\u002Fcsv-report",", а точка входа — ",[14,34,35],{},"merge_csv.py",":",[38,39,45],"pre",{"className":40,"code":42,"language":43,"meta":44},[41],"language-text","\u002Fopt\u002Fcsv-report\u002F\n  .venv\u002Fbin\u002Fpython\n  merge_csv.py\n  data\u002Finbox\u002F\n  data\u002Fout\u002F\n","text","",[14,46,42],{"__ignoreMap":44},[10,48,49],{},"Создайте виртуальное окружение и установите зависимости, если они есть:",[38,51,55],{"className":52,"code":53,"language":54,"meta":44,"style":44},"language-bash shiki shiki-themes github-light github-dark","cd \u002Fopt\u002Fcsv-report\npython3 -m venv .venv\n.venv\u002Fbin\u002Fpython -m pip install -r requirements.txt\n.venv\u002Fbin\u002Fpython merge_csv.py\n","bash",[14,56,57,70,86,106],{"__ignoreMap":44},[58,59,62,66],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"sj4cs","cd",[58,67,69],{"class":68},"sZZnC"," \u002Fopt\u002Fcsv-report\n",[58,71,73,77,80,83],{"class":60,"line":72},2,[58,74,76],{"class":75},"sScJk","python3",[58,78,79],{"class":64}," -m",[58,81,82],{"class":68}," venv",[58,84,85],{"class":68}," .venv\n",[58,87,89,92,94,97,100,103],{"class":60,"line":88},3,[58,90,91],{"class":75},".venv\u002Fbin\u002Fpython",[58,93,79],{"class":64},[58,95,96],{"class":68}," pip",[58,98,99],{"class":68}," install",[58,101,102],{"class":64}," -r",[58,104,105],{"class":68}," requirements.txt\n",[58,107,109,111],{"class":60,"line":108},4,[58,110,91],{"class":75},[58,112,113],{"class":68}," merge_csv.py\n",[10,115,116,117,120],{},"Убедитесь, что скрипт завершается с кодом ",[14,118,119],{},"0",", читает нужные входные файлы и сохраняет результат туда, куда ожидается. Планировщик не исправит неверные пути, отсутствующие переменные окружения или права на папку.",[10,122,123,124,127,128,131],{},"Создайте отдельного непривилегированного пользователя ",[14,125,126],{},"automation"," и выдайте ему чтение проекта и запись только в папку результата. Не запускайте регулярную задачу от ",[14,129,130],{},"root",", если ей не нужны системные привилегии.",[22,133,135],{"id":134},"опишите-одноразовый-сервис","Опишите одноразовый сервис",[10,137,138,139,36],{},"Создайте ",[14,140,141],{},"\u002Fetc\u002Fsystemd\u002Fsystem\u002Fcsv-report.service",[38,143,147],{"className":144,"code":145,"language":146,"meta":44,"style":44},"language-ini shiki shiki-themes github-light github-dark","[Unit]\nDescription=Build the scheduled CSV report\n\n[Service]\nType=oneshot\nUser=automation\nGroup=automation\nWorkingDirectory=\u002Fopt\u002Fcsv-report\nExecStart=\u002Fopt\u002Fcsv-report\u002F.venv\u002Fbin\u002Fpython \u002Fopt\u002Fcsv-report\u002Fmerge_csv.py\nUMask=0077\nNoNewPrivileges=true\nPrivateTmp=true\nProtectSystem=strict\nProtectHome=true\nReadWritePaths=\u002Fopt\u002Fcsv-report\u002Fdata\u002Fout\n","ini",[14,148,149,154,159,165,170,176,182,188,194,200,206,212,218,224,230],{"__ignoreMap":44},[58,150,151],{"class":60,"line":61},[58,152,153],{},"[Unit]\n",[58,155,156],{"class":60,"line":72},[58,157,158],{},"Description=Build the scheduled CSV report\n",[58,160,161],{"class":60,"line":88},[58,162,164],{"emptyLinePlaceholder":163},true,"\n",[58,166,167],{"class":60,"line":108},[58,168,169],{},"[Service]\n",[58,171,173],{"class":60,"line":172},5,[58,174,175],{},"Type=oneshot\n",[58,177,179],{"class":60,"line":178},6,[58,180,181],{},"User=automation\n",[58,183,185],{"class":60,"line":184},7,[58,186,187],{},"Group=automation\n",[58,189,191],{"class":60,"line":190},8,[58,192,193],{},"WorkingDirectory=\u002Fopt\u002Fcsv-report\n",[58,195,197],{"class":60,"line":196},9,[58,198,199],{},"ExecStart=\u002Fopt\u002Fcsv-report\u002F.venv\u002Fbin\u002Fpython \u002Fopt\u002Fcsv-report\u002Fmerge_csv.py\n",[58,201,203],{"class":60,"line":202},10,[58,204,205],{},"UMask=0077\n",[58,207,209],{"class":60,"line":208},11,[58,210,211],{},"NoNewPrivileges=true\n",[58,213,215],{"class":60,"line":214},12,[58,216,217],{},"PrivateTmp=true\n",[58,219,221],{"class":60,"line":220},13,[58,222,223],{},"ProtectSystem=strict\n",[58,225,227],{"class":60,"line":226},14,[58,228,229],{},"ProtectHome=true\n",[58,231,233],{"class":60,"line":232},15,[58,234,235],{},"ReadWritePaths=\u002Fopt\u002Fcsv-report\u002Fdata\u002Fout\n",[10,237,238,239,242,243,246,247,249,250,253,254,257],{},"Путь в ",[14,240,241],{},"ExecStart"," должен указывать прямо на Python из виртуального окружения: systemd не запускает интерактивную оболочку и не активирует ",[14,244,245],{},".venv"," автоматически. Проверьте, что ",[14,248,126],{}," может читать ",[14,251,252],{},"data\u002Finbox",", а папка ",[14,255,256],{},"data\u002Fout"," существует и доступна для записи.",[10,259,260,261,264],{},"Если программе нужны переменные окружения, задайте отдельный файл с ограниченными правами и добавьте ",[14,262,263],{},"EnvironmentFile=\u002Fetc\u002Fcsv-report.env",". Не помещайте токены и пароли в публичный репозиторий или командную строку процесса.",[22,266,268],{"id":267},"добавьте-расписание","Добавьте расписание",[10,270,138,271,36],{},[14,272,273],{},"\u002Fetc\u002Fsystemd\u002Fsystem\u002Fcsv-report.timer",[38,275,277],{"className":144,"code":276,"language":146,"meta":44,"style":44},"[Unit]\nDescription=Run the CSV report every day\n\n[Timer]\nOnCalendar=*-*-* 08:30:00\nPersistent=true\nRandomizedDelaySec=5m\nUnit=csv-report.service\n\n[Install]\nWantedBy=timers.target\n",[14,278,279,283,288,292,297,302,307,312,317,321,326],{"__ignoreMap":44},[58,280,281],{"class":60,"line":61},[58,282,153],{},[58,284,285],{"class":60,"line":72},[58,286,287],{},"Description=Run the CSV report every day\n",[58,289,290],{"class":60,"line":88},[58,291,164],{"emptyLinePlaceholder":163},[58,293,294],{"class":60,"line":108},[58,295,296],{},"[Timer]\n",[58,298,299],{"class":60,"line":172},[58,300,301],{},"OnCalendar=*-*-* 08:30:00\n",[58,303,304],{"class":60,"line":178},[58,305,306],{},"Persistent=true\n",[58,308,309],{"class":60,"line":184},[58,310,311],{},"RandomizedDelaySec=5m\n",[58,313,314],{"class":60,"line":190},[58,315,316],{},"Unit=csv-report.service\n",[58,318,319],{"class":60,"line":196},[58,320,164],{"emptyLinePlaceholder":163},[58,322,323],{"class":60,"line":202},[58,324,325],{},"[Install]\n",[58,327,328],{"class":60,"line":208},[58,329,330],{},"WantedBy=timers.target\n",[10,332,333,336,337,340,341,344],{},[14,334,335],{},"OnCalendar"," задаёт локальное календарное время сервера. ",[14,338,339],{},"Persistent=true"," позволяет выполнить пропущенный календарный запуск после повторной активации timer. ",[14,342,343],{},"RandomizedDelaySec"," добавляет небольшой случайный сдвиг, чтобы множество задач не стартовали строго одновременно. Это не обещание запуска до секунды: systemd допускает окно точности таймера.",[10,346,347],{},"До установки проверьте выражение:",[38,349,351],{"className":52,"code":350,"language":54,"meta":44,"style":44},"systemd-analyze calendar '*-*-* 08:30:00'\ntimedatectl\n",[14,352,353,364],{"__ignoreMap":44},[58,354,355,358,361],{"class":60,"line":61},[58,356,357],{"class":75},"systemd-analyze",[58,359,360],{"class":68}," calendar",[58,362,363],{"class":68}," '*-*-* 08:30:00'\n",[58,365,366],{"class":60,"line":72},[58,367,368],{"class":75},"timedatectl\n",[10,370,371],{},"Перечитайте units и включите расписание:",[38,373,375],{"className":52,"code":374,"language":54,"meta":44,"style":44},"sudo systemctl daemon-reload\nsudo systemctl enable --now csv-report.timer\nsystemctl list-timers csv-report.timer\n",[14,376,377,388,403],{"__ignoreMap":44},[58,378,379,382,385],{"class":60,"line":61},[58,380,381],{"class":75},"sudo",[58,383,384],{"class":68}," systemctl",[58,386,387],{"class":68}," daemon-reload\n",[58,389,390,392,394,397,400],{"class":60,"line":72},[58,391,381],{"class":75},[58,393,384],{"class":68},[58,395,396],{"class":68}," enable",[58,398,399],{"class":64}," --now",[58,401,402],{"class":68}," csv-report.timer\n",[58,404,405,408,411],{"class":60,"line":88},[58,406,407],{"class":75},"systemctl",[58,409,410],{"class":68}," list-timers",[58,412,402],{"class":68},[10,414,415,416,418,419,422,423,430,431,434],{},"Выражение ",[14,417,335],{}," и поведение ",[14,420,421],{},"Persistent"," описаны в ",[424,425,429],"a",{"href":426,"rel":427},"https:\u002F\u002Fwww.freedesktop.org\u002Fsoftware\u002Fsystemd\u002Fman\u002Flatest\u002Fsystemd.timer.html",[428],"nofollow","официальной документации systemd timer","; синтаксис календаря проверяет команда ",[14,432,433],{},"systemd-analyze calendar",".",[22,436,438],{"id":437},"проверьте-запуск-и-разберите-сбой","Проверьте запуск и разберите сбой",[10,440,441],{},"Не ждите следующего утра: запустите сервис вручную и посмотрите его статус и журнал:",[38,443,445],{"className":52,"code":444,"language":54,"meta":44,"style":44},"sudo systemctl start csv-report.service\nsystemctl status csv-report.service\njournalctl -u csv-report.service --since today\n",[14,446,447,459,468],{"__ignoreMap":44},[58,448,449,451,453,456],{"class":60,"line":61},[58,450,381],{"class":75},[58,452,384],{"class":68},[58,454,455],{"class":68}," start",[58,457,458],{"class":68}," csv-report.service\n",[58,460,461,463,466],{"class":60,"line":72},[58,462,407],{"class":75},[58,464,465],{"class":68}," status",[58,467,458],{"class":68},[58,469,470,473,476,479,482],{"class":60,"line":88},[58,471,472],{"class":75},"journalctl",[58,474,475],{"class":64}," -u",[58,477,478],{"class":68}," csv-report.service",[58,480,481],{"class":64}," --since",[58,483,484],{"class":68}," today\n",[10,486,487,488,491,492,495],{},"Для такой задачи ",[14,489,490],{},"print()"," и стандартный Python ",[14,493,494],{},"logging"," попадут в журнал systemd. Добавляйте туда статус и количество обработанных записей, но не содержимое строк, персональные данные или секреты.",[10,497,498,499,502,503,505,506,508],{},"Если сервис не сработал, проверьте по порядку: абсолютные пути, ",[14,500,501],{},"WorkingDirectory",", права пользователя ",[14,504,126],{},", зависимости в ",[14,507,245],{},", переменные окружения и доступность входных файлов. Ошибка задачи не должна считаться успешным пустым отчётом. Для важных операций добавьте в сам скрипт понятный код завершения, безопасные повторы и уведомление об ошибке.",[10,510,511],{},"Таймер не заменяет мониторинг результата: смотрите время последнего успешного выполнения и проверяйте, что отчёт действительно обновился. Для задачи дольше интервала запуска отдельно продумайте защиту от параллельного выполнения и повторную обработку.",[22,513,515],{"id":514},"соберите-автоматизацию-целиком","Соберите автоматизацию целиком",[10,517,518,519,523,524,528,529,533],{},"Расписание полезно, когда скрипт уже умеет проверять данные и сохранять воспроизводимый результат. В ",[424,520,522],{"href":521},"\u002Fru\u002Fblog\u002Fobedinit-csv-faily-python\u002F","примере объединения CSV-файлов на Python"," показана обработка входных выгрузок, а в ",[424,525,527],{"href":526},"\u002Fru\u002Fblog\u002Fparsers-scrapy-python\u002F","руководстве по Python-парсерам"," — получение и проверка данных из разрешённых источников. Оба материала входят в ",[424,530,532],{"href":531},"\u002Fru\u002Fcourses\u002Fpython-automation\u002F","бесплатный курс Python для автоматизации",", где эти шаги собраны в проектную последовательность.",[535,536,537],"style",{},"html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":44,"searchDepth":72,"depth":72,"links":539},[540,541,542,543,544],{"id":24,"depth":72,"text":25},{"id":134,"depth":72,"text":135},{"id":267,"depth":72,"text":268},{"id":437,"depth":72,"text":438},{"id":514,"depth":72,"text":515},"python-automation","course","2026-10-12","Настройте запуск Python по расписанию через systemd timer: сервисный пользователь, виртуальное окружение, права на файлы, проверка времени и просмотр логов.","md","educational",{},"\u002Fimages\u002Fog\u002Fblog\u002Fru\u002Fzapusk-python-po-raspisaniyu-linux.png","\u002Fru\u002Fblog\u002Fzapusk-python-po-raspisaniyu-linux","запуск Python скрипта по расписанию Linux","7 мин",[557],"flatrentby",null,{"title":5,"description":548},"zapusk-python-po-raspisaniyu-linux","ru\u002Fblog\u002Fzapusk-python-po-raspisaniyu-linux",[563,564,16,565],"Python","Linux","Автоматизация","hVRi4NAuK5T0S-dctZInAxCtxd_azoT251quzq_Bn-8",1791825743742]