Minimal Ubuntu servers, containers, and stripped-down desktop installs often reach for curl before the command is actually present. To install curl on Ubuntu Linux, use the default APT package for the safest system integration, the Snap package when you specifically want the store build with experimental HTTP/3 support, or a signed GitHub release source build when you need upstream curl in a separate prefix.
Ubuntu 26.04 LTS, 24.04 LTS, and 22.04 LTS all provide curl in the default APT repositories. The GitHub release method in this workflow exposes the upstream build as curl-source so it does not replace Ubuntu’s package-managed /usr/bin/curl.
Install curl on Ubuntu Linux
Pick one method for your normal workflow. APT is the default recommendation for scripts, repository setup, package downloads, and general command-line use because it updates with the rest of Ubuntu and has normal access to your filesystem.
Choose a curl Installation Method
| Method | Source or Channel | Command Exposed | Update Behavior | Best Fit |
|---|---|---|---|---|
| APT package | Ubuntu main repository | /usr/bin/curl | Updated through normal APT updates | Recommended for most Ubuntu systems, shell scripts, repository setup, and downloads that need normal file access |
| Snap package | Snapcraft cURL package, published by Yuzukosho (aoilinux) | snap run curl, with /snap/bin/curl after Snap paths load | Updated through snap refresh | Useful when you want the Snap build and its experimental HTTP/3 support, but it has confinement limits for hidden files and paths outside allowed areas |
| GitHub release source build | curl/curl GitHub releases | /usr/local/bin/curl-source | Rerun sudo update-curl-source | Advanced users who need the current upstream release in an isolated OpenSSL-based build without shadowing Ubuntu’s /usr/bin/curl |
Keep the APT method unless you have a specific reason to choose another source. The Snap package is not presented as the upstream official package by curl itself, and the source build creates a separate command so scripts that expect Ubuntu’s packaged
curlkeep working.
Update Ubuntu Package Lists
Refresh APT metadata before installing the default package or Snap support packages:
sudo apt update
These commands use
sudofor package-management tasks that need root privileges. If your account cannot run sudo yet, follow the steps to add a user to sudoers on Ubuntu before continuing.
Install curl with APT
Install the Ubuntu package with APT. This installs the command-line tool and the matching runtime library dependencies from Ubuntu’s repositories:
sudo apt install curl
Confirm the installed package state, command path, version line, and a simple HTTPS request:
dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package} ${Version}\n' curl
command -v curl
curl --version | sed -n '1p'
curl -fsS -o /dev/null -w 'HTTP status: %{http_code}\n' https://example.com
Relevant output on Ubuntu 26.04 includes:
ii curl 8.18.0-1ubuntu2.1 /usr/bin/curl curl 8.18.0 (x86_64-pc-linux-gnu) libcurl/8.18.0 OpenSSL/3.5.5 zlib/1.3.1 brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 nghttp2/1.68.0 librtmp/2.3 mit-krb5/1.22.1 OpenLDAP/2.6.10 HTTP status: 200
Current APT package versions in the supported Ubuntu repositories are:
| Ubuntu Release | APT curl Package | Version Line Starts With |
|---|---|---|
| Ubuntu 26.04 LTS (resolute) | 8.18.0-1ubuntu2.1 | curl 8.18.0 |
| Ubuntu 24.04 LTS (noble) | 8.5.0-2ubuntu10.9 | curl 8.5.0 |
| Ubuntu 22.04 LTS (jammy) | 7.81.0-1ubuntu1.24 | curl 7.81.0 |
The curl package is the command-line tool. Development headers for compiling software against libcurl are separate from the basic command and are not required for these commands.
Install curl with Snap
Ubuntu desktop installs usually already include Snap support, but minimal or customized systems can differ. Install or repair snapd, then wait for Snap seeding before installing the package:
sudo apt install snapd
sudo snap wait system seed.loaded
sudo snap install curl
Verify the Snap package and run curl through snap run, which avoids relying on whether the current shell has already loaded /snap/bin:
snap list curl
snap run curl --version 2>/dev/null | sed -n '1p'
snap run curl -fsS -o /dev/null -w 'HTTP status: %{http_code}\n' https://example.com 2>/dev/null
Relevant output from the current stable channel is:
Name Version Rev Tracking Publisher Notes curl 8.20.0 2404 latest/stable aoilinux - curl 8.20.0 (x86_64-pc-linux-gnu) libcurl/8.20.0 OpenSSL/4.0.0 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 libssh2/1.11.0 nghttp2/1.68.1 ngtcp2/1.22.0 nghttp3/1.15.0 mit-krb5/1.20.1 OpenLDAP/2.6.10 HTTP status: 200
The Snap package prints a first-run caution about confinement and labels its HTTP/3 support as experimental. It may not access hidden directories, many paths outside your home directory, or restricted areas in the same way as the APT package. Use the APT package for generic installer scripts unless you have already tested the Snap package against the paths the script needs.
Build curl from GitHub Releases
The source method resolves the latest non-prerelease curl GitHub release, downloads the .tar.xz release asset and detached signature, imports Daniel Stenberg’s release signing key from Ubuntu’s keyserver, verifies the signature, builds with OpenSSL, and installs under /opt/curl-source/releases. The public command is curl-source, not curl, so package-managed scripts keep using /usr/bin/curl.
Create the update helper:
sudo tee /usr/local/bin/update-curl-source > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
export DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"
API_URL="${CURL_GITHUB_API_URL:-https://api.github.com/repos/curl/curl/releases/latest}"
ROOT_DIR="${CURL_SOURCE_ROOT:-/opt/curl-source}"
RELEASES_DIR="${ROOT_DIR}/releases"
CURRENT_LINK="${ROOT_DIR}/current"
BIN_LINK="${CURL_SOURCE_BIN_LINK:-/usr/local/bin/curl-source}"
KEY_FPR="27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2"
KEY_URL="https://keyserver.ubuntu.com/pks/lookup?op=get&options=mr&search=0x${KEY_FPR}"
if [ "${EUID}" -ne 0 ]; then
echo "Run this helper with sudo: sudo update-curl-source" >&2
exit 1
fi
if ! command -v apt-get >/dev/null 2>&1; then
echo "This helper expects an Ubuntu system with apt-get." >&2
exit 1
fi
echo "Installing curl build prerequisites..."
apt-get update
apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
gpg \
libpsl-dev \
libssl-dev \
pkg-config \
python3 \
tar \
xz-utils \
zlib1g-dev
for cmd in gcc gpg grep make nproc pkg-config python3 sed tar xz; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Required command is missing after prerequisite installation: $cmd" >&2
exit 1
fi
done
release_info=$(
python3 - "$API_URL" <<'PY'
import json
import re
import sys
import urllib.request
api_url = sys.argv[1]
req = urllib.request.Request(
api_url,
headers={
"Accept": "application/vnd.github+json",
"User-Agent": "linuxcapable-curl-source-installer",
},
)
with urllib.request.urlopen(req, timeout=45) as response:
release = json.load(response)
if release.get("draft") or release.get("prerelease"):
raise SystemExit("Latest GitHub release is a draft or prerelease; stop before downloading.")
tag = release.get("tag_name", "")
match = re.fullmatch(r"curl-(\d+)_(\d+)_(\d+)", tag)
if not match:
raise SystemExit(f"Unexpected curl release tag: {tag!r}")
version = ".".join(match.groups())
archive_name = f"curl-{version}.tar.xz"
signature_name = f"{archive_name}.asc"
assets = {asset.get("name"): asset.get("browser_download_url") for asset in release.get("assets", [])}
archive_url = assets.get(archive_name)
signature_url = assets.get(signature_name)
if not archive_url or not signature_url:
raise SystemExit(f"Could not find {archive_name} and {signature_name} in the latest release assets.")
print(version)
print(archive_name)
print(archive_url)
print(signature_url)
PY
)
mapfile -t release_fields <<<"$release_info"
if [ "${#release_fields[@]}" -ne 4 ]; then
echo "Could not parse release metadata from GitHub." >&2
exit 1
fi
version="${release_fields[0]}"
archive_name="${release_fields[1]}"
archive_url="${release_fields[2]}"
signature_url="${release_fields[3]}"
release_dir="${RELEASES_DIR}/${version}"
installed_version="none"
if [ -x "${CURRENT_LINK}/bin/curl" ]; then
installed_version=$("${CURRENT_LINK}/bin/curl" --version | sed -n '1s/^curl \([^ ]*\).*/\1/p')
fi
if [ "$installed_version" = "$version" ]; then
echo "curl source build is already current: ${version}"
if [ -e "$BIN_LINK" ] && [ ! -L "$BIN_LINK" ]; then
echo "$BIN_LINK exists and is not a symlink. Move it before updating the curl source helper." >&2
exit 1
fi
ln -sfn "${CURRENT_LINK}/bin/curl" "$BIN_LINK"
"$BIN_LINK" --version | sed -n '1p'
exit 0
fi
work_dir=$(mktemp -d)
install_finished=no
release_existed=no
if [ -e "$release_dir" ]; then
release_existed=yes
fi
cleanup() {
rm -rf "$work_dir"
if [ "$install_finished" != yes ] && [ "$release_existed" = no ] && [ -n "${release_dir:-}" ]; then
rm -rf "$release_dir"
fi
}
trap cleanup EXIT
download_file() {
local url=$1
local output=$2
python3 - "$url" "$output" <<'PY'
import os
import sys
import urllib.request
url, output = sys.argv[1:]
tmp = f"{output}.part"
req = urllib.request.Request(url, headers={"User-Agent": "linuxcapable-curl-source-installer"})
with urllib.request.urlopen(req, timeout=120) as response, open(tmp, "wb") as target:
while True:
chunk = response.read(1024 * 1024)
if not chunk:
break
target.write(chunk)
os.replace(tmp, output)
PY
}
cd "$work_dir"
echo "Downloading curl ${version} from GitHub releases..."
download_file "$archive_url" "$archive_name"
download_file "$signature_url" "${archive_name}.asc"
gpg_home="${work_dir}/gnupg"
install -d -m 0700 "$gpg_home"
export GNUPGHOME="$gpg_home"
echo "Importing Daniel Stenberg's curl release signing key..."
download_file "$KEY_URL" "daniel-stenberg.asc"
gpg --batch --import "daniel-stenberg.asc"
gpg --batch --with-colons --fingerprint "$KEY_FPR" | grep -Fq "fpr:::::::::${KEY_FPR}:"
echo "Verifying release signature..."
gpg --batch --verify "${archive_name}.asc" "$archive_name"
if [ -e "$release_dir" ]; then
if [ -x "${release_dir}/bin/curl" ]; then
echo "Using existing build directory: $release_dir"
else
echo "$release_dir exists but does not contain bin/curl. Remove or inspect it before rerunning." >&2
exit 1
fi
else
tar -xJf "$archive_name"
cd "curl-${version}"
echo "Configuring curl ${version}..."
LDFLAGS="-Wl,-rpath,${release_dir}/lib" ./configure \
--prefix="$release_dir" \
--with-openssl \
--with-ca-path=/etc/ssl/certs \
--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt
echo "Building curl ${version}..."
make -j"$(nproc)"
echo "Installing curl ${version} to ${release_dir}..."
make install
fi
"${release_dir}/bin/curl" --version | sed -n '1p'
"${release_dir}/bin/curl" -fsS https://example.com >/dev/null
if command -v ldd >/dev/null 2>&1; then
ldd "${release_dir}/bin/curl" | grep -F "${release_dir}/lib/libcurl" >/dev/null
fi
install -d -m 0755 "$ROOT_DIR"
if [ -e "$CURRENT_LINK" ] && [ ! -L "$CURRENT_LINK" ]; then
echo "$CURRENT_LINK exists and is not a symlink. Move it before rerunning." >&2
exit 1
fi
ln -sfn "$release_dir" "$CURRENT_LINK"
if [ -e "$BIN_LINK" ] && [ ! -L "$BIN_LINK" ]; then
echo "$BIN_LINK exists and is not a symlink. Move it before rerunning." >&2
exit 1
fi
ln -sfn "${CURRENT_LINK}/bin/curl" "$BIN_LINK"
install_finished=yes
echo "curl ${version} installed as ${BIN_LINK}."
"$BIN_LINK" --version | sed -n '1p'
EOF
sudo chmod 0755 /usr/local/bin/update-curl-source
Run the helper, then verify the separate upstream command:
sudo update-curl-source
curl-source --version | sed -n '1p'
curl-source -fsS -o /dev/null -w 'HTTP status: %{http_code}\n' https://example.com
readlink -f "$(command -v curl-source)"
ldd "$(readlink -f "$(command -v curl-source)")" | grep '/opt/curl-source/.*/lib/libcurl'
On Ubuntu 26.04, relevant output from the current release follows this shape. The exact curl version changes when curl publishes a newer stable release, and older Ubuntu releases show their own OpenSSL, zlib, and libpsl library versions:
Verifying release signature... curl 8.20.0 (x86_64-pc-linux-gnu) libcurl/8.20.0 OpenSSL/3.5.5 zlib/1.3.1 libpsl/0.21.2 curl 8.20.0 installed as /usr/local/bin/curl-source. HTTP status: 200 /opt/curl-source/releases/8.20.0/bin/curl libcurl.so.4 => /opt/curl-source/releases/8.20.0/lib/libcurl.so.4
Run the helper again later to check for a new upstream release. When the installed source build already matches GitHub’s latest release, it exits without rebuilding:
sudo update-curl-source
curl source build is already current: 8.20.0 curl 8.20.0 (x86_64-pc-linux-gnu) libcurl/8.20.0 OpenSSL/3.5.5 zlib/1.3.1 libpsl/0.21.2
Use curl on Ubuntu
Use the command name that matches your install method. APT users normally run curl, Snap users can run snap run curl for reliable path handling, and source-build users run curl-source.
Download a file with APT-installed curl:
curl -fsS -o example.html https://example.com
Check an HTTP status code without saving the page body:
curl -fsS -o /dev/null -w 'HTTP status: %{http_code}\n' https://example.com
HTTP status: 200
For option patterns such as headers, redirects, uploads, JSON requests, and safer download flags, use the full curl command examples reference after the package is installed.
Update or Remove curl on Ubuntu
Update and remove curl through the same source you used to install it. Do not expect APT to update a Snap package or the source-built curl-source command.
Update APT-Installed curl
Refresh package metadata, then upgrade only the curl package if an update is available:
sudo apt update
sudo apt install --only-upgrade curl
Update Snap-Installed curl
Snap updates normally run automatically, but you can request a refresh for the curl snap manually:
sudo snap refresh curl
Update Source-Built curl
Rerun the helper to resolve the current GitHub release, verify the signed archive, and refresh the curl-source symlink when a newer release exists:
sudo update-curl-source
Remove APT-Installed curl
Remove the Ubuntu package with APT:
sudo apt remove curl
Verify that the package is no longer installed:
dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' curl 2>/dev/null | grep '^ii' || echo "curl is not installed through APT"
curl is not installed through APT
Remove Snap-Installed curl
Remove the Snap package and its automatic recovery snapshot:
sudo snap remove --purge curl
Confirm the snap is gone:
snap list curl 2>/dev/null || echo "curl snap is not installed"
curl snap is not installed
Remove Source-Built curl
The next commands permanently delete the source-built curl releases under
/opt/curl-source, thecurl-sourcesymlink, and the update helper. Keep any local patches or build notes before removing that directory.
sudo rm -rf /opt/curl-source
sudo rm -f /usr/local/bin/curl-source
sudo rm -f /usr/local/bin/update-curl-source
hash -r
command -v curl-source || echo "curl-source command removed"
curl-source command removed
Troubleshoot curl on Ubuntu
Fix curl: command not found
If your shell cannot find curl, check the install source before reinstalling. A Snap install may be present even when the bare curl command is not on the current PATH, and the source-build method exposes curl-source instead of curl.
command -v curl || echo "curl is not in PATH"
command -v curl-source || echo "curl-source is not in PATH"
dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' curl 2>/dev/null | grep '^ii' || echo "APT curl package is not installed"
snap list curl 2>/dev/null || echo "curl snap is not installed"
Install the APT package when you need the normal curl command in /usr/bin:
sudo apt update
sudo apt install curl
Fix Snap curl File Access Limits
The Snap package can print a caution about restricted access to hidden directories and paths outside allowed areas. If an installer script, dotfile workflow, or system path download fails only with the Snap package, switch to the APT package for that workflow:
sudo snap remove --purge curl
sudo apt update
sudo apt install curl
Fix curl Could Not Resolve Host
The error curl: (6) Could not resolve host usually means the hostname did not resolve through DNS, or the URL was typed incorrectly. Check name resolution before changing curl options:
getent hosts example.com
curl -fsS https://example.com >/dev/null
If name resolution fails for several domains, use the dedicated walkthrough to fix curl could not resolve host errors.
Fix Source Build Dependency Errors
The source helper installs the packages it needs, but damaged package metadata or interrupted dependency setup can still break configure or build steps. Reinstall the build prerequisites, then rerun the helper:
sudo apt update
sudo apt install build-essential ca-certificates gpg libpsl-dev libssl-dev pkg-config python3 tar xz-utils zlib1g-dev
sudo update-curl-source
For a broader toolchain setup, the GCC compiler on Ubuntu workflow covers the compiler package family used by source builds.
curl Resources for Ubuntu
- Ubuntu curl package search shows the current archive package versions for supported Ubuntu releases.
- Snapcraft cURL package page lists the Snap publisher, channel, update date, and confinement notes.
- curl GitHub releases provide the release assets used by the source helper.
- curl release verification documents the release signing workflow and signing key.
- curl source installation documentation explains the upstream configure, build, and install flow.
Conclusion
curl is installed on Ubuntu with a verified package source, command path, update owner, and removal path. Keep APT for normal scripts and downloads, use Snap only when its confinement fits the workflow, or keep the GitHub release build isolated as curl-source. Next, use curl command examples to build safer download and API checks.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>