You can now run a full ICTFax server with a single command. Official images are published to Docker Hub and GitHub Container Registry, rebuilt from source on every commit, and they carry the whole stack: Rocky Linux 8, FreeSWITCH 1.10.12 with T.38, PHP, MariaDB, the fax toolchain and the web dashboard.

docker run -d --name ictfax \
  -p 8080:80 -p 8443:443 \
  -p 5060:5060/udp -p 5060:5060/tcp \
  -p 16384-16484:16384-16484/udp \
  ictinnovations/ictfax:latest

Give it a minute or two. The container provisions its database, loads the schema, generates a TLS certificate and starts FreeSWITCH before the dashboard comes up on http://localhost:8080/. Sign in with admin@ictcore.org and helloAdmin, then change it.

Why this took a while

ICTFax has always been installable. The installation guide works, and plenty of people have followed it. What it asks of you is a couple of hours and a willingness to debug somebody else’s repository setup on a machine you have not decided to keep yet.

That is a bad first impression for a project you are only evaluating. Most people who want to know whether an open source fax server suits them want to send one fax and look at the result. Making that a two hour commitment loses the ones who would have stayed.

So the image exists to shorten the distance between curiosity and a working fax. It is not a replacement for a considered production install, though it is close enough that some people will run it in production anyway, and we have built it so that is a reasonable thing to do.

Diagram of the ICTFax container showing Apache with mod_php, FreeSWITCH 1.10.12 with mod_spandsp, MariaDB, the fax toolchain of tiff2pdf, tiffcp, Ghostscript, ImageMagick and sox, the ICTCore framework and supervisord on Rocky Linux 8

What is inside

The interesting parts are the ones you would otherwise assemble by hand.

  • FreeSWITCH 1.10.12 with mod_spandsp, so T.38 and G.711 fax both work out of the box. The packages come from Fedora Copr, which is HTTPS and GPG signed, rather than from a mirror whose signing key nobody publishes.
  • The fax toolchain. tiff2pdf, tiffcp, Ghostscript, ImageMagick and sox. ICTFax shells out to these for document conversion, and on a bare Rocky 8 box half of them live in a repository that ships disabled.
  • MariaDB, provisioned on first boot. Point DB_HOST at your own server and the bundled one drops out of supervisord completely, so you are not paying for a database you do not use.
  • A TLS certificate generated at runtime. Baking a private key into a public image would hand every user the same key, so the entrypoint makes one on first start.

Useful environment variables: ICTCORE_HOST for the hostname the dashboard advertises, the DB_* set for an external database, and FS_ESL_PASSWORD for the FreeSWITCH event socket. Worth mounting: /usr/ictcore/data for received faxes, /usr/ictcore/log, and /var/lib/mysql if you keep the bundled database.

The build tests itself

Every push runs the image through a smoke test before anything reaches a registry. The container has to boot, the dashboard has to answer, Angular deep links have to resolve, the REST API has to respond without a PHP fatal, mod_spandsp has to be loaded in FreeSWITCH, and the schema has to be present in the database. Any failure stops the run before the publish step.

Flow showing a GitHub push building the image, booting it and running a smoke test covering the dashboard, REST API, mod_spandsp and database schema, then publishing to Docker Hub and GHCR only if every check passes

That gate turned out to be worth more than the image. Building it meant running a clean ICTFax install on a fresh machine over and over, which is something almost nobody does, and it surfaced four real bugs in the first-install path. Invalid DEFAULT clauses in one of the schema files, a trigger that got created twice, seed files loading in the wrong order, and a database connection string that glued the host and port together in a way the mysqli extension has never accepted.

All four are fixed upstream in ICTCore, so they are fixed for people installing from packages too, not just for container users. That is the part we did not expect and the reason the test gate stays.

Where the images live

Both registries carry the same build:

  • docker pull ictinnovations/ictfax:latest from Docker Hub
  • docker pull ghcr.io/ictinnovations/ictfax:latest from GitHub Container Registry

Two sibling images went out at the same time. ictinnovations/ictcore is the framework on its own, useful if you are building against the REST API and do not need a fax dashboard. ictinnovations/ictdialer is the voice and fax broadcasting product on the same stack.

What we would still do by hand

Media matters more than signalling with fax. Publish the RTP range, or you will get a call that connects and a fax that never completes, which is the classic first run complaint and looks like a software fault when it is a port mapping.

SIP trunk configuration is still yours. The image gives you a working FreeSWITCH, not a working carrier relationship, and T.38 negotiation with a given provider is the part that takes the afternoon. The technologies page covers what ICTFax expects from a trunk.

And if you are running this for real, put a reverse proxy with a certificate you trust in front of it rather than relying on the self-signed one the entrypoint generates.

Frequently asked questions

Does this replace the manual installation?

No. The installation guide is still the right path if you want ICTFax on a host you already run and manage with your own tooling. The image is the fastest way to get a working system to look at.

Can I use my own database?

Yes. Set DB_HOST, DB_PORT, DB_NAME, DB_USER and DB_PASS. The bundled MariaDB is removed from supervisord automatically when DB_HOST points somewhere else.

Which ports do I need to publish?

80 and 443 for the dashboard and REST API, 5060 on TCP and UDP for SIP, and the 16384 to 16484 UDP range for media. Skipping the media range is the most common reason a first fax fails.

Is T.38 supported?

Yes. mod_spandsp ships in the image and the build refuses to publish if it is not loaded. G.711 pass-through faxing works as well.

How do I persist faxes across restarts?

Mount /usr/ictcore/data. Add /var/lib/mysql if you are using the bundled database and /usr/ictcore/log if you want logs on the host.

Is the image free to use?

Yes. ICTFax is open source under GPL-3.0 and the images carry no additional restrictions. Professional support is available separately if you want it.

Try it and tell us what breaks

The images are new. They pass their own tests on a clean runner, which is not the same as working on every network anyone will point them at. If you hit something, the support page has the places to raise it, and a bug report from a container is easier for us to reproduce than one from a bespoke install.