{ pkgs, lib, ... }: let # Andorra - the smallest dataset in Europe (3.1 MB) osmData = pkgs.fetchurl { url = "https://web.archive.org/web/20250430211212/https://download.geofabrik.de/europe/andorra-latest.osm.pbf"; hash = "sha256-Ey+ipTOFUm80rxBteirPW5N4KxmUsg/pCE58E/2rcyE="; }; in { name = "nominatim"; meta = { maintainers = with lib.teams; [ geospatial ngi ]; }; nodes = { # nominatim - self contained host nominatim = { config, pkgs, ... }: { # Nominatim services.nominatim = { enable = true; hostName = "nominatim"; settings = { NOMINATIM_IMPORT_STYLE = "admin"; }; ui = { config = '' Nominatim_Config.Page_Title='Test Nominatim instance'; Nominatim_Config.Nominatim_API_Endpoint='https://localhost/'; ''; }; }; # Disable SSL services.nginx.virtualHosts.nominatim = { forceSSL = false; enableACME = false; }; # Database services.postgresql = { enableTCPIP = true; authentication = lib.mkForce '' local all all trust host all all 0.0.0.0/0 md5 host all all ::0/0 md5 ''; }; systemd.services.postgresql-setup.postStart = '' psql --command "ALTER ROLE \"nominatim-api\" WITH PASSWORD 'password';" ''; networking.firewall.allowedTCPPorts = [ config.services.postgresql.settings.port ]; }; # api - web API only api = { config, pkgs, ... }: { # Database password system.activationScripts = { passwordFile.text = with config.services.nominatim.database; '' mkdir -p /run/secrets echo "${host}:${toString port}:${dbname}:${apiUser}:password" \ > /run/secrets/pgpass chown nominatim-api:nominatim-api /run/secrets/pgpass chmod 0600 /run/secrets/pgpass ''; }; # Nominatim services.nominatim = { enable = true; hostName = "nominatim"; settings = { NOMINATIM_LOG_DB = "yes"; }; database = { host = "nominatim"; passwordFile = "/run/secrets/pgpass"; extraConnectionParams = "application_name=nominatim;connect_timeout=2"; }; }; # Disable SSL services.nginx.virtualHosts.nominatim = { forceSSL = false; enableACME = false; }; }; }; testScript = '' # Test nominatim host nominatim.start() nominatim.wait_for_unit("nominatim.service") # Import OSM data nominatim.succeed(""" cd /tmp sudo -u nominatim \ NOMINATIM_DATABASE_WEBUSER=nominatim-api \ NOMINATIM_IMPORT_STYLE=admin \ nominatim import --continue import-from-file --osm-file ${osmData} """) nominatim.succeed("systemctl restart nominatim.service") # Test CLI nominatim.succeed("sudo -u nominatim-api nominatim search --query Andorra") # Test web API nominatim.succeed("curl 'http://localhost/status' | grep OK") nominatim.succeed(""" curl "http://localhost/search?q=Andorra&format=geojson" | grep "Andorra" curl "http://localhost/reverse?lat=42.5407167&lon=1.5732033&format=geojson" """) # Test UI nominatim.succeed(""" curl "http://localhost/ui/search.html" \ | grep "Nominatim Demo" """) # Test api host api.start() api.wait_for_unit("nominatim.service") # Test web API api.succeed(""" curl "http://localhost/search?q=Andorra&format=geojson" | grep "Andorra" curl "http://localhost/reverse?lat=42.5407167&lon=1.5732033&format=geojson" """) # Test format rewrites # Redirect / to search nominatim.succeed(""" curl --verbose "http://localhost" 2>&1 \ | grep "Location: http://localhost/ui/search.html" """) # Return text by default nominatim.succeed(""" curl --verbose "http://localhost/status" 2>&1 \ | grep "Content-Type: text/plain" """) # Return JSON by default nominatim.succeed(""" curl --verbose "http://localhost/search?q=Andorra" 2>&1 \ | grep "Content-Type: application/json" """) # Return XML by default nominatim.succeed(""" curl --verbose "http://localhost/lookup" 2>&1 \ | grep "Content-Type: text/xml" curl --verbose "http://localhost/reverse?lat=0&lon=0" 2>&1 \ | grep "Content-Type: text/xml" """) # Redirect explicitly requested HTML format nominatim.succeed(""" curl --verbose "http://localhost/search?format=html" 2>&1 \ | grep "Location: http://localhost/ui/search.html" curl --verbose "http://localhost/reverse?format=html" 2>&1 \ | grep "Location: http://localhost/ui/reverse.html" """) # Return explicitly requested JSON format nominatim.succeed(""" curl --verbose "http://localhost/search?format=json" 2>&1 \ | grep "Content-Type: application/json" curl --verbose "http://localhost/reverse?format=json" 2>&1 \ | grep "Content-Type: application/json" """) ''; }