diff --git a/latexrun b/latexrun
index 557a93c..d5598e9 100755
--- a/latexrun
+++ b/latexrun
@@ -158,7 +158,7 @@ def main():
                            args.obj_dir, args.nowarns)
         task_commit = LaTeXCommit(db, task_latex, args.output)
         task_bibtex = BibTeX(db, task_latex, args.bibtex_cmd, args.bibtex_args,
-                             args.nowarns)
+                             args.nowarns, args.obj_dir)
         tasks = [task_latex, task_commit, task_bibtex]
         stable = run_tasks(tasks, args.max_iterations)
 
@@ -1471,12 +1471,13 @@ class LaTeXFilter:
 #
 
 class BibTeX(Task):
-    def __init__(self, db, latex_task, cmd, cmd_args, nowarns):
+    def __init__(self, db, latex_task, cmd, cmd_args, nowarns, obj_dir):
         super().__init__(db, 'bibtex::' + normalize_input_path(
             latex_task.get_tex_filename()))
         self.__latex_task = latex_task
         self.__cmd = cmd
         self.__cmd_args = cmd_args
+        self.__obj_dir = obj_dir
 
     def stable(self):
         # If bibtex doesn't have its inputs, then it's stable because
@@ -1512,6 +1513,10 @@ class BibTeX(Task):
            re.search(r'^\\bibdata\{',  aux_data, flags=re.M):
             return True
 
+        if re.search(r'^\\abx@aux@cite\{', aux_data, flags=re.M):
+            # biber citation
+            return True
+
         # Recurse into included aux files (see aux_input_command), in
         # case \bibliography appears in an \included file.
         for m in re.finditer(r'^\\@input\{([^}]*)\}', aux_data, flags=re.M):
@@ -1522,7 +1527,10 @@ class BibTeX(Task):
         return False
 
     def _input_args(self):
-        aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux'
+        if self.__is_biber():
+            aux_name = os.path.basename(self.__latex_task.get_jobname())
+        else:
+            aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux'
         return [self.__cmd] + self.__cmd_args + [aux_name]
 
     def _input_cwd(self):
@@ -1539,7 +1547,8 @@ class BibTeX(Task):
                 h = hashlib.sha256()
                 for line in aux:
                     if line.startswith((b'\\citation{', b'\\bibdata{',
-                                        b'\\bibstyle{', b'\\@input{')):
+                                        b'\\bibstyle{', b'\\@input{',
+                                        b'\\abx@aux@cite{')):
                         h.update(line)
                 return h.hexdigest()
         except FileNotFoundError:
@@ -1552,6 +1561,9 @@ class BibTeX(Task):
             return first + ':'
         return first + ':' + rest
 
+    def __is_biber(self):
+        return "biber" in self.__cmd
+
     def _execute(self):
         # This gets complicated when \include is involved.  \include
         # switches to a different aux file and records its path in the
@@ -1583,7 +1595,7 @@ class BibTeX(Task):
         except OSError as e:
             raise TaskError('failed to execute bibtex task: ' + str(e)) from e
 
-        inputs, auxnames = self.__parse_inputs(stdout, cwd, env)
+        inputs, auxnames, outbase = self.__parse_inputs(stdout, cwd, env)
         if not inputs and not auxnames:
             # BibTeX failed catastrophically.
             print(stdout, file=sys.stderr)
@@ -1599,7 +1611,8 @@ class BibTeX(Task):
         for path in inputs:
             self._input('file', path)
 
-        outbase = auxnames[0][:-4]
+        if self.__is_biber():
+            outbase = os.path.join(cwd, outbase)
         outputs = [outbase + '.bbl', outbase + '.blg']
         return RunResult(outputs, {'outbase': outbase, 'status': status,
                                    'inputs': inputs})
@@ -1635,6 +1648,7 @@ class BibTeX(Task):
         kpathsea = Kpathsea('bibtex')
         inputs = []
         auxnames = []
+        outbase = None
         for line in log.splitlines():
             m = re.match('(?:The top-level auxiliary file:'
                          '|A level-[0-9]+ auxiliary file:) (.*)', line)
@@ -1660,7 +1674,21 @@ class BibTeX(Task):
 
                 inputs.append(filename)
 
-        return inputs, auxnames
+            # biber output
+            m = re.search("Found BibTeX data source '(.*?)'",
+                         line)
+            if m:
+                filename = m.group(1)
+                inputs.append(filename)
+
+            m = re.search("Logfile is '(.*?)'", line)
+            if m:
+                outbase = m.group(1)[:-4]
+
+        if outbase is None:
+            outbase = auxnames[0][:-4]
+
+        return inputs, auxnames, outbase
 
     def report(self):
         extra = self._get_result_extra()
@@ -1779,6 +1807,21 @@ class BibTeXFilter:
         if match('Aborted at line ([0-9]+) of file (.*)'):
             return ('info', m.group(2), int(m.group(1)), 'aborted')
 
+        # biber type errors
+        if match('^.*> WARN - (.*)$'):
+            print ('warning', None, None, m.group(1))
+            m2 = re.match("(.*) in file '(.*?)', skipping ...", m.group(1))
+            if m2:
+                return ('warning', m2.group(2), "0", m2.group(1))
+            return ('warning', None, None, m.group(1))
+
+        if match('^.*> ERROR - (.*)$'):
+            m2 = re.match("BibTeX subsystem: (.*?), line (\d+), (.*)$", m.group(1))
+            if m2:
+                return ('error', m2.group(1), m2.group(2), m2.group(3))
+            return ('error', None, None, m.group(1))
+
+
     def __canonicalize(self, msg):
         if msg.startswith('Warning'):
             msg = re.sub('^Warning-*', '', msg)
diff --git a/test/T-biblatex-biber-fail/main.bib b/test/T-biblatex-biber-fail/main.bib
new file mode 100644
index 0000000..8d2245c
--- /dev/null
+++ b/test/T-biblatex-biber-fail/main.bib
@@ -0,0 +1,4 @@
+@misc{ent1,
+  author = {Author},
+  title = {Title}
+}
diff --git a/test/T-biblatex-biber-fail/main.tex b/test/T-biblatex-biber-fail/main.tex
new file mode 100644
index 0000000..190c355
--- /dev/null
+++ b/test/T-biblatex-biber-fail/main.tex
@@ -0,0 +1,19 @@
+% Test biblatex
+
+\documentclass{article}
+\usepackage[backend=biber]{biblatex}
+\addbibresource{main.bib}
+
+\begin{document}
+    \cite{ent2}
+    \printbibliography
+\end{document}
+
+%% bibtex-cmd: biber
+%% output:
+%% main.tex:8: warning: Citation 'ent2' on page 1 undefined
+%% main.tex:9: warning: Empty bibliography
+%% main.tex: warning: There were undefined references
+%% main.tex: warning: [biblatex] Please (re)run Biber on the file: main and rerun LaTeX afterwards
+%% warning None None I didn't find a database entry for 'ent2' (section 0)
+%% <no file>: warning: I didn't find a database entry for 'ent2' (section 0)
diff --git a/test/T-biblatex-biber/main.bib b/test/T-biblatex-biber/main.bib
new file mode 100644
index 0000000..8d2245c
--- /dev/null
+++ b/test/T-biblatex-biber/main.bib
@@ -0,0 +1,4 @@
+@misc{ent1,
+  author = {Author},
+  title = {Title}
+}
diff --git a/test/T-biblatex-biber/main.tex b/test/T-biblatex-biber/main.tex
new file mode 100644
index 0000000..3bdd4ff
--- /dev/null
+++ b/test/T-biblatex-biber/main.tex
@@ -0,0 +1,12 @@
+% Test biblatex
+
+\documentclass{article}
+\usepackage[backend=biber]{biblatex}
+\addbibresource{main.bib}
+
+\begin{document}
+    \cite{ent1}
+    \printbibliography
+\end{document}
+
+%% bibtex-cmd: biber
diff --git a/test/T-biblatex-bibtex/main.bib b/test/T-biblatex-bibtex/main.bib
new file mode 100644
index 0000000..8d2245c
--- /dev/null
+++ b/test/T-biblatex-bibtex/main.bib
@@ -0,0 +1,4 @@
+@misc{ent1,
+  author = {Author},
+  title = {Title}
+}
diff --git a/test/T-biblatex-bibtex/main.tex b/test/T-biblatex-bibtex/main.tex
new file mode 100644
index 0000000..f55a24c
--- /dev/null
+++ b/test/T-biblatex-bibtex/main.tex
@@ -0,0 +1,10 @@
+% Test biblatex
+
+\documentclass{article}
+\usepackage[backend=bibtex]{biblatex}
+\addbibresource{main.bib}
+
+\begin{document}
+    \cite{ent1}
+    \printbibliography
+\end{document}
diff --git a/test/run b/test/run
index 3562da1..4b553d6 100755
--- a/test/run
+++ b/test/run
@@ -69,6 +69,11 @@ def test(latexrun_path, latexrun_args, input_path):
     else:
         status_expect = 0
 
+    m = re.search(pre + 'bibtex-cmd: (.*)', input_src, re.I|re.M)
+    if m:
+        bibtex_cmd = m.group(1)
+        latexrun_args += ["--bibtex-cmd",  bibtex_cmd]
+
     m = re.search(pre + 'output:\n((?:' + pre + '.*\n)*)', input_src, re.I|re.M)
     if m:
         output_expect = re.sub(pre, '', m.group(1), flags=re.M)