diff --git a/mlrun/db/filedb.py b/mlrun/db/filedb.py
index 68df353b9..b5b4d199f 100644
--- a/mlrun/db/filedb.py
+++ b/mlrun/db/filedb.py
@@ -15,7 +15,7 @@
 import json
 import pathlib
 from datetime import datetime, timedelta
-from os import makedirs, path, remove
+from os import makedirs, path, remove, scandir
 
 import yaml
 
@@ -31,6 +31,7 @@ from .base import RunDBError, RunDBInterface
 run_logs = 'runs'
 artifacts_dir = 'artifacts'
 functions_dir = 'functions'
+schedules_dir = 'schedules'
 
 
 class FileRunDB(RunDBInterface):
@@ -41,6 +42,7 @@ class FileRunDB(RunDBInterface):
         self.dirpath = dirpath
         self._datastore = None
         self._subpath = None
+        makedirs(self.schedules_dir, exist_ok=True)
 
     def connect(self, secrets=None):
         sm = StoreManager(secrets)
@@ -257,6 +259,25 @@ class FileRunDB(RunDBInterface):
         return path.join(self.dirpath, '{}/{}/{}{}'.format(
             table, project, tag, key))
 
+    @property
+    def schedules_dir(self):
+        return path.join(self.dirpath, schedules_dir)
+
+    def store_schedule(self, data):
+        sched_id = 1 + sum(1 for _ in scandir(self.schedules_dir))
+        fname = path.join(
+            self.schedules_dir,
+            '{}{}'.format(sched_id, self.format),
+        )
+        with open(fname, 'w') as out:
+            out.write(self._dumps(data))
+
+    def list_schedules(self):
+        pattern = '*{}'.format(self.format)
+        for p in pathlib.Path(self.schedules_dir).glob(pattern):
+            with p.open() as fp:
+                yield self._loads(fp.read())
+
     _encodings = {
         '.yaml': ('to_yaml', dict_to_yaml),
         '.json': ('to_json', dict_to_json),
diff --git a/mlrun/db/httpd.py b/mlrun/db/httpd.py
index aae3bbf58..43c0cfb26 100644
--- a/mlrun/db/httpd.py
+++ b/mlrun/db/httpd.py
@@ -125,6 +125,10 @@ def submit_job():
         return json_error(HTTPStatus.BAD_REQUEST, reason='bad JSON body')
 
     logger.info('submit_job: {}'.format(data))
+    _submit(data)
+
+
+def _submit(data):
     task = data.get('task')
     function = data.get('function')
     url = data.get('functionUrl')
@@ -162,6 +166,7 @@ def submit_job():
         if schedule:
             args = (task, )
             job_id = _scheduler.add(schedule, fn, args)
+            _db.save_schedule(data)
             resp = {'schedule': schedule, 'id': job_id}
         else:
             resp = fn.run(task)
@@ -704,6 +709,11 @@ def init_app():
     periodic.schedule(task, 60)
 
     _scheduler = Scheduler()
+    for data in _db.list_schedules():
+        if 'schedule' not in data:
+            logger.warning('bad scheduler data - %s', data)
+            continue
+        _submit(data)
 
 
 # Don't remove this function, it's an entry point in setup.py
diff --git a/mlrun/db/sqldb.py b/mlrun/db/sqldb.py
index b87f24cf5..f364742ab 100644
--- a/mlrun/db/sqldb.py
+++ b/mlrun/db/sqldb.py
@@ -116,6 +116,12 @@ with warnings.catch_warnings():
         start_time = Column(TIMESTAMP)
         labels = relationship(Label)
 
+    class Schedule(Base, HasStruct):
+        __tablename__ = 'schedules'
+
+        id = Column(Integer, primary_key=True)
+        body = Column(BLOB)
+
 
 class SQLDB(RunDBInterface):
     def __init__(self, dsn):
@@ -328,6 +334,14 @@ class SQLDB(RunDBInterface):
             Artifact.project == project).distinct()
         return [row[0] for row in query]
 
+    def store_schedule(self, data):
+        sched = Schedule()
+        sched.struct = data
+        self._upsert(sched)
+
+    def list_schedules(self):
+        return [s.struct for s in self.session.query(Schedule)]
+
     def _query(self, cls, **kw):
         kw = {k: v for k, v in kw.items() if v}
         return self.session.query(cls).filter_by(**kw)
diff --git a/mlrun/funcdoc.py b/mlrun/funcdoc.py
index 513426a9c..3d54e0c22 100644
--- a/mlrun/funcdoc.py
+++ b/mlrun/funcdoc.py
@@ -107,14 +107,16 @@ def rst_read_doc(lines):
 
 
 def rst_read_section(lines, i):
-    # Skip empty lines
-    while not lines[i].strip() and i < len(lines):
-        i += 1
-
-    # :param path: The path of the file to wrap
-    match = re.match(r':(\w+)(\s+\w+)?:', lines[i])
-    if not match:
-        raise ValueError(f'{i}: bad line - {lines[i]!r}')
+    # Skip empty lines/other lines
+    for i, line in enumerate(lines[i:], i):
+        if not line.strip():
+            continue
+        # :param path: The path of the file to wrap
+        match = re.match(r':\s*(\w+)(\s+\w+)?\s*:', lines[i])
+        if match:
+            break
+    else:
+        return None
 
     tag = match.group(1)
     value = match.group(2).strip() if match.group(2) else ''
@@ -134,7 +136,10 @@ def parse_rst(docstring: str):
     ret = param_dict()
 
     while i != -1:
-        tag, value, text, i = rst_read_section(lines, i)
+        out = rst_read_section(lines, i)
+        if not out:
+            break
+        tag, value, text, i = out
         if tag == 'param':
             params[value] = param_dict(name=value, doc=text)
             names.append(value)

Persistancy for cron jobs
We'd like to be able to reload schedules after restart
diff --git a/tests/rst_cases.yml b/tests/rst_cases.yml
index 3bbd601ba..6953f4934 100644
--- a/tests/rst_cases.yml
+++ b/tests/rst_cases.yml
@@ -78,3 +78,47 @@
         type: BufferedFileStorage
         default: ""
     lineno: -1
+- name: space in colon
+  text: |
+    Open a file
+
+    :param path : The path of the file to wrap
+  expected:
+    doc: Open a file
+    params:
+      - name: path
+        doc: The path of the file to wrap
+        type: ""
+        default: ""
+    ret:
+        name: ""
+        doc: ""
+        type: ""
+        default: ""
+    lineno: -1
+- name: Data in middle
+  text: |
+    Open a file
+
+    :param path : The path of the file to wrap
+
+    Then we can optionally
+
+    :param mode : File mode
+  expected:
+    doc: Open a file
+    params:
+      - name: path
+        doc: The path of the file to wrap
+        type: ""
+        default: ""
+      - name: mode
+        doc: File mode
+        type: ""
+        default: ""
+    ret:
+        name: ""
+        doc: ""
+        type: ""
+        default: ""
+    lineno: -1
diff --git a/tests/test_filedb.py b/tests/test_filedb.py
index 2cd3feb5e..6f3fb2b58 100644
--- a/tests/test_filedb.py
+++ b/tests/test_filedb.py
@@ -45,3 +45,14 @@ def test_list_fuctions(db: FileRunDB):
 
     out = db.list_functions('', proj)
     assert len(out) == count, 'bad list'
+
+
+def test_schedules(db: FileRunDB):
+    count = 7
+    for i in range(count):
+        data = {'i': i}
+        db.store_schedule(data)
+
+    scheds = list(db.list_schedules())
+    assert count == len(scheds), 'wrong number of schedules'
+    assert set(range(count)) == set(s['i'] for s in scheds), 'bad scheds'
diff --git a/tests/test_sqldb.py b/tests/test_sqldb.py
index ff161f6bb..6639a92c4 100644
--- a/tests/test_sqldb.py
+++ b/tests/test_sqldb.py
@@ -189,3 +189,14 @@ def test_list_runs(db: sqldb.SQLDB):
 
     runs = list(db.list_runs(uid=uid, iter=True))
     assert 5 == len(runs), 'iter=True'
+
+
+def test_schedules(db: sqldb.SQLDB):
+    count = 7
+    for i in range(count):
+        data = {'i': i}
+        db.store_schedule(data)
+
+    scheds = list(db.list_schedules())
+    assert count == len(scheds), 'wrong number of schedules'
+    assert set(range(count)) == set(s['i'] for s in scheds), 'bad scheds'

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohttp==3.8.6
aiosignal==1.3.1
anyio==3.7.1
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
async-timeout==4.0.3
asynctest==0.13.0
attrs==24.2.0
backcall==0.2.0
beautifulsoup4==4.13.3
bleach==6.0.0
boto3==1.33.13
botocore==1.33.13
cachetools==5.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
click==8.1.8
cloudpickle==2.2.1
croniter==0.3.31
debugpy==1.7.0
decorator==5.1.1
defusedxml==0.7.1
Deprecated==1.2.18
docstring_parser==0.16
entrypoints==0.4
exceptiongroup==1.2.2
fastjsonschema==2.21.1
fire==0.7.0
Flask==2.2.5
frozenlist==1.3.3
gevent==1.4.0
gitdb==4.0.12
GitPython==3.1.44
google-api-core==2.24.2
google-auth==2.38.0
google-cloud-core==2.4.3
google-cloud-storage==3.1.0
google-crc32c==1.5.0
google-resumable-media==2.7.2
googleapis-common-protos==1.69.2
greenlet==3.1.1
gunicorn==19.9.0
idna==3.10
importlib-metadata==6.7.0
importlib-resources==5.12.0
iniconfig==2.0.0
ipykernel==6.16.2
ipython==7.34.0
ipython-genutils==0.2.0
itsdangerous==2.1.2
jedi==0.19.2
Jinja2==3.1.6
jmespath==1.0.1
jsonschema==4.17.3
jupyter-server==1.24.0
jupyter_client==7.4.9
jupyter_core==4.12.0
jupyterlab-pygments==0.2.2
kfp==1.4.0
kfp-pipeline-spec==0.1.16
kfp-server-api==1.8.5
kubernetes==11.0.0
MarkupSafe==2.1.5
matplotlib-inline==0.1.6
mistune==3.0.2
-e git+https://github.com/mlrun/mlrun.git@f7ae101674a6d96c147c0515022459f6b4e31b78#egg=mlrun
multidict==6.0.5
nbclassic==1.2.0
nbclient==0.7.4
nbconvert==7.6.0
nbformat==5.8.0
nest-asyncio==1.6.0
notebook==6.5.7
notebook_shim==0.2.4
nuclio-jupyter==0.10.4
nuclio_sdk==0.5.15
numpy==1.21.6
oauthlib==3.2.2
packaging==24.0
pandas==1.3.5
pandocfilters==1.5.1
parso==0.8.4
pexpect==4.9.0
pickleshare==0.7.5
pkgutil_resolve_name==1.3.10
pluggy==1.2.0
prometheus-client==0.17.1
prompt_toolkit==3.0.48
proto-plus==1.26.1
protobuf==3.20.3
psutil==7.0.0
ptyprocess==0.7.0
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycparser==2.21
Pygments==2.17.2
pyrsistent==0.19.3
pytest==7.4.4
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.1
pyzmq==26.2.1
requests==2.31.0
requests-oauthlib==2.0.0
requests-toolbelt==1.0.0
rsa==4.9
s3transfer==0.8.2
Send2Trash==1.8.3
six==1.17.0
smmap==5.0.2
sniffio==1.3.1
soupsieve==2.4.1
SQLAlchemy==1.3.11
strip-hints==0.1.13
tabulate==0.8.3
termcolor==2.3.0
terminado==0.17.1
tinycss2==1.2.1
tomli==2.0.1
tornado==6.2
traitlets==5.9.0
typing_extensions==4.7.1
urllib3==1.26.20
wcwidth==0.2.13
webencodings==0.5.1
websocket-client==1.6.1
Werkzeug==2.2.3
wrapt==1.16.0
yarl==1.9.4
zipp==3.15.0

name: mlrun
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==3.8.6
      - aiosignal==1.3.1
      - anyio==3.7.1
      - argon2-cffi==23.1.0
      - argon2-cffi-bindings==21.2.0
      - async-timeout==4.0.3
      - asynctest==0.13.0
      - attrs==24.2.0
      - backcall==0.2.0
      - beautifulsoup4==4.13.3
      - bleach==6.0.0
      - boto3==1.33.13
      - botocore==1.33.13
      - cachetools==5.5.2
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - cloudpickle==2.2.1
      - croniter==0.3.31
      - debugpy==1.7.0
      - decorator==5.1.1
      - defusedxml==0.7.1
      - deprecated==1.2.18
      - docstring-parser==0.16
      - entrypoints==0.4
      - exceptiongroup==1.2.2
      - fastjsonschema==2.21.1
      - fire==0.7.0
      - flask==2.2.5
      - frozenlist==1.3.3
      - gevent==1.4.0
      - gitdb==4.0.12
      - gitpython==3.1.44
      - google-api-core==2.24.2
      - google-auth==2.38.0
      - google-cloud-core==2.4.3
      - google-cloud-storage==3.1.0
      - google-crc32c==1.5.0
      - google-resumable-media==2.7.2
      - googleapis-common-protos==1.69.2
      - greenlet==3.1.1
      - gunicorn==19.9.0
      - idna==3.10
      - importlib-metadata==6.7.0
      - importlib-resources==5.12.0
      - iniconfig==2.0.0
      - ipykernel==6.16.2
      - ipython==7.34.0
      - ipython-genutils==0.2.0
      - itsdangerous==2.1.2
      - jedi==0.19.2
      - jinja2==3.1.6
      - jmespath==1.0.1
      - jsonschema==4.17.3
      - jupyter-client==7.4.9
      - jupyter-core==4.12.0
      - jupyter-server==1.24.0
      - jupyterlab-pygments==0.2.2
      - kfp==1.4.0
      - kfp-pipeline-spec==0.1.16
      - kfp-server-api==1.8.5
      - kubernetes==11.0.0
      - markupsafe==2.1.5
      - matplotlib-inline==0.1.6
      - mistune==3.0.2
      - multidict==6.0.5
      - nbclassic==1.2.0
      - nbclient==0.7.4
      - nbconvert==7.6.0
      - nbformat==5.8.0
      - nest-asyncio==1.6.0
      - notebook==6.5.7
      - notebook-shim==0.2.4
      - nuclio-jupyter==0.10.4
      - nuclio-sdk==0.5.15
      - numpy==1.21.6
      - oauthlib==3.2.2
      - packaging==24.0
      - pandas==1.3.5
      - pandocfilters==1.5.1
      - parso==0.8.4
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pkgutil-resolve-name==1.3.10
      - pluggy==1.2.0
      - prometheus-client==0.17.1
      - prompt-toolkit==3.0.48
      - proto-plus==1.26.1
      - protobuf==3.20.3
      - psutil==7.0.0
      - ptyprocess==0.7.0
      - pyasn1==0.5.1
      - pyasn1-modules==0.3.0
      - pycparser==2.21
      - pygments==2.17.2
      - pyrsistent==0.19.3
      - pytest==7.4.4
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.1
      - pyzmq==26.2.1
      - requests==2.31.0
      - requests-oauthlib==2.0.0
      - requests-toolbelt==1.0.0
      - rsa==4.9
      - s3transfer==0.8.2
      - send2trash==1.8.3
      - six==1.17.0
      - smmap==5.0.2
      - sniffio==1.3.1
      - soupsieve==2.4.1
      - sqlalchemy==1.3.11
      - strip-hints==0.1.13
      - tabulate==0.8.3
      - termcolor==2.3.0
      - terminado==0.17.1
      - tinycss2==1.2.1
      - tomli==2.0.1
      - tornado==6.2
      - traitlets==5.9.0
      - typing-extensions==4.7.1
      - urllib3==1.26.20
      - wcwidth==0.2.13
      - webencodings==0.5.1
      - websocket-client==1.6.1
      - werkzeug==2.2.3
      - wrapt==1.16.0
      - yarl==1.9.4
      - zipp==3.15.0
prefix: /opt/conda/envs/mlrun

tests/test_filedb.py::test_schedules tests/test_sqldb.py::test_schedules
tests/test_filedb.py::test_save_get_function tests/test_filedb.py::test_list_fuctions tests/test_sqldb.py::test_save_get_function tests/test_sqldb.py::test_list_functions tests/test_sqldb.py::test_log tests/test_sqldb.py::test_runs tests/test_sqldb.py::test_update_run tests/test_sqldb.py::test_artifacts tests/test_sqldb.py::test_list_artifact_tags tests/test_sqldb.py::test_list_projects tests/test_sqldb.py::test_list_runs
mlrun/db/filedb.py mlrun/db/sqldb.py mlrun/db/httpd.py mlrun/funcdoc.py
mlrun/db/filedb.py mlrun/db/sqldb.py mlrun/db/httpd.py mlrun/funcdoc.py