How to Install curl on Ubuntu 26.04, 24.04 and 22.04

Last updated Monday, May 18, 2026 10:23 am Joshua James 6 min read

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

MethodSource or ChannelCommand ExposedUpdate BehaviorBest Fit
APT packageUbuntu main repository/usr/bin/curlUpdated through normal APT updatesRecommended for most Ubuntu systems, shell scripts, repository setup, and downloads that need normal file access
Snap packageSnapcraft cURL package, published by Yuzukosho (aoilinux)snap run curl, with /snap/bin/curl after Snap paths loadUpdated through snap refreshUseful 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 buildcurl/curl GitHub releases/usr/local/bin/curl-sourceRerun sudo update-curl-sourceAdvanced 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 curl keep working.

Update Ubuntu Package Lists

Refresh APT metadata before installing the default package or Snap support packages:

sudo apt update

These commands use sudo for 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 ReleaseAPT curl PackageVersion Line Starts With
Ubuntu 26.04 LTS (resolute)8.18.0-1ubuntu2.1curl 8.18.0
Ubuntu 24.04 LTS (noble)8.5.0-2ubuntu10.9curl 8.5.0
Ubuntu 22.04 LTS (jammy)7.81.0-1ubuntu1.24curl 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, the curl-source symlink, 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

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.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: